Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/protobuf/json_format.py: 14%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

484 statements  

1# Protocol Buffers - Google's data interchange format 

2# Copyright 2008 Google Inc. All rights reserved. 

3# 

4# Use of this source code is governed by a BSD-style 

5# license that can be found in the LICENSE file or at 

6# https://developers.google.com/open-source/licenses/bsd 

7 

8"""Contains routines for printing protocol messages in JSON format. 

9 

10Simple usage example: 

11 

12 # Create a proto object and serialize it to a json format string. 

13 message = my_proto_pb2.MyMessage(foo='bar') 

14 json_string = json_format.MessageToJson(message) 

15 

16 # Parse a json format string to proto object. 

17 message = json_format.Parse(json_string, my_proto_pb2.MyMessage()) 

18""" 

19 

20__author__ = 'jieluo@google.com (Jie Luo)' 

21 

22 

23import base64 

24from collections import OrderedDict 

25import json 

26import math 

27from operator import methodcaller 

28import re 

29 

30from google.protobuf import descriptor 

31from google.protobuf import message_factory 

32from google.protobuf import symbol_database 

33from google.protobuf.internal import type_checkers 

34 

35 

36_INT_TYPES = frozenset([ 

37 descriptor.FieldDescriptor.CPPTYPE_INT32, 

38 descriptor.FieldDescriptor.CPPTYPE_UINT32, 

39 descriptor.FieldDescriptor.CPPTYPE_INT64, 

40 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

41]) 

42_INT64_TYPES = frozenset([ 

43 descriptor.FieldDescriptor.CPPTYPE_INT64, 

44 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

45]) 

46_FLOAT_TYPES = frozenset([ 

47 descriptor.FieldDescriptor.CPPTYPE_FLOAT, 

48 descriptor.FieldDescriptor.CPPTYPE_DOUBLE, 

49]) 

50_INFINITY = 'Infinity' 

51_NEG_INFINITY = '-Infinity' 

52_NAN = 'NaN' 

53 

54_UNPAIRED_SURROGATE_PATTERN = re.compile( 

55 '[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]' 

56) 

57 

58_VALID_EXTENSION_NAME = re.compile(r'\[[a-zA-Z0-9\._]*\]$') 

59 

60 

61class Error(Exception): 

62 """Top-level module error for json_format.""" 

63 

64 

65class SerializeToJsonError(Error): 

66 """Thrown if serialization to JSON fails.""" 

67 

68 

69class ParseError(Error): 

70 """Thrown in case of parsing error.""" 

71 

72 

73class EnumStringValueParseError(ParseError): 

74 """Thrown if unknown string enum value is encountered. 

75 

76 This exception is suppressed if ignore_unknown_fields is set. 

77 """ 

78 

79 

80def MessageToJson( 

81 message, 

82 preserving_proto_field_name=False, 

83 indent=2, 

84 sort_keys=False, 

85 use_integers_for_enums=False, 

86 descriptor_pool=None, 

87 float_precision=None, 

88 ensure_ascii=True, 

89 always_print_fields_with_no_presence=False, 

90): 

91 """Converts protobuf message to JSON format. 

92 

93 Args: 

94 message: The protocol buffers message instance to serialize. 

95 always_print_fields_with_no_presence: If True, fields without presence 

96 (implicit presence scalars, repeated fields, and map fields) will always 

97 be serialized. Any field that supports presence is not affected by this 

98 option (including singular message fields and oneof fields). 

99 preserving_proto_field_name: If True, use the original proto field names as 

100 defined in the .proto file. If False, convert the field names to 

101 lowerCamelCase. 

102 indent: The JSON object will be pretty-printed with this indent level. An 

103 indent level of 0 or negative will only insert newlines. If the indent 

104 level is None, no newlines will be inserted. 

105 sort_keys: If True, then the output will be sorted by field names. 

106 use_integers_for_enums: If true, print integers instead of enum names. 

107 descriptor_pool: A Descriptor Pool for resolving types. If None use the 

108 default. 

109 float_precision: Deprecated. If set, use this to specify float field valid 

110 digits. 

111 ensure_ascii: If True, strings with non-ASCII characters are escaped. If 

112 False, Unicode strings are returned unchanged. 

113 

114 Returns: 

115 A string containing the JSON formatted protocol buffer message. 

116 """ 

117 printer = _Printer( 

118 preserving_proto_field_name, 

119 use_integers_for_enums, 

120 descriptor_pool, 

121 float_precision, 

122 always_print_fields_with_no_presence, 

123 ) 

124 return printer.ToJsonString(message, indent, sort_keys, ensure_ascii) 

125 

126 

127def MessageToDict( 

128 message, 

129 always_print_fields_with_no_presence=False, 

130 preserving_proto_field_name=False, 

131 use_integers_for_enums=False, 

132 descriptor_pool=None, 

133 float_precision=None, 

134): 

135 """Converts protobuf message to a dictionary. 

136 

137 When the dictionary is encoded to JSON, it conforms to proto3 JSON spec. 

138 

139 Args: 

140 message: The protocol buffers message instance to serialize. 

141 always_print_fields_with_no_presence: If True, fields without presence 

142 (implicit presence scalars, repeated fields, and map fields) will always 

143 be serialized. Any field that supports presence is not affected by this 

144 option (including singular message fields and oneof fields). 

145 preserving_proto_field_name: If True, use the original proto field names as 

146 defined in the .proto file. If False, convert the field names to 

147 lowerCamelCase. 

148 use_integers_for_enums: If true, print integers instead of enum names. 

149 descriptor_pool: A Descriptor Pool for resolving types. If None use the 

150 default. 

151 float_precision: Deprecated. If set, use this to specify float field valid 

152 digits. 

153 

154 Returns: 

155 A dict representation of the protocol buffer message. 

156 """ 

157 printer = _Printer( 

158 preserving_proto_field_name, 

159 use_integers_for_enums, 

160 descriptor_pool, 

161 float_precision, 

162 always_print_fields_with_no_presence, 

163 ) 

164 # pylint: disable=protected-access 

165 return printer._MessageToJsonObject(message) 

166 

167 

168def _IsMapEntry(field): 

169 return ( 

170 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE 

171 and field.message_type.has_options 

172 and field.message_type.GetOptions().map_entry 

173 ) 

174 

175 

176class _Printer(object): 

177 """JSON format printer for protocol message.""" 

178 

179 def __init__( 

180 self, 

181 preserving_proto_field_name=False, 

182 use_integers_for_enums=False, 

183 descriptor_pool=None, 

184 float_precision=None, 

185 always_print_fields_with_no_presence=False, 

186 ): 

187 self.always_print_fields_with_no_presence = ( 

188 always_print_fields_with_no_presence 

189 ) 

190 self.preserving_proto_field_name = preserving_proto_field_name 

191 self.use_integers_for_enums = use_integers_for_enums 

192 self.descriptor_pool = descriptor_pool 

193 if float_precision: 

194 self.float_format = '.{}g'.format(float_precision) 

195 else: 

196 self.float_format = None 

197 

198 def ToJsonString(self, message, indent, sort_keys, ensure_ascii): 

199 js = self._MessageToJsonObject(message) 

200 return json.dumps( 

201 js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii 

202 ) 

203 

204 def _MessageToJsonObject(self, message): 

205 """Converts message to an object according to Proto3 JSON Specification.""" 

206 message_descriptor = message.DESCRIPTOR 

207 full_name = message_descriptor.full_name 

208 if _IsWrapperMessage(message_descriptor): 

209 return self._WrapperMessageToJsonObject(message) 

210 if full_name in _WKTJSONMETHODS: 

211 return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self) 

212 js = {} 

213 return self._RegularMessageToJsonObject(message, js) 

214 

215 def _RegularMessageToJsonObject(self, message, js): 

216 """Converts normal message according to Proto3 JSON Specification.""" 

217 fields = message.ListFields() 

218 

219 try: 

220 for field, value in fields: 

221 if self.preserving_proto_field_name: 

222 name = field.name 

223 else: 

224 name = field.json_name 

225 if _IsMapEntry(field): 

226 # Convert a map field. 

227 v_field = field.message_type.fields_by_name['value'] 

228 js_map = {} 

229 for key in value: 

230 if isinstance(key, bool): 

231 if key: 

232 recorded_key = 'true' 

233 else: 

234 recorded_key = 'false' 

235 else: 

236 recorded_key = str(key) 

237 js_map[recorded_key] = self._FieldToJsonObject(v_field, value[key]) 

238 js[name] = js_map 

239 elif field.is_repeated: 

240 # Convert a repeated field. 

241 js[name] = [self._FieldToJsonObject(field, k) for k in value] 

242 elif field.is_extension: 

243 name = '[%s]' % field.full_name 

244 js[name] = self._FieldToJsonObject(field, value) 

245 else: 

246 js[name] = self._FieldToJsonObject(field, value) 

247 

248 # Serialize default value if including_default_value_fields is True. 

249 if ( 

250 self.always_print_fields_with_no_presence 

251 ): 

252 message_descriptor = message.DESCRIPTOR 

253 for field in message_descriptor.fields: 

254 

255 # always_print_fields_with_no_presence doesn't apply to 

256 # any field which supports presence. 

257 if self.always_print_fields_with_no_presence and field.has_presence: 

258 continue 

259 

260 if self.preserving_proto_field_name: 

261 name = field.name 

262 else: 

263 name = field.json_name 

264 if name in js: 

265 # Skip the field which has been serialized already. 

266 continue 

267 if _IsMapEntry(field): 

268 js[name] = {} 

269 elif field.is_repeated: 

270 js[name] = [] 

271 else: 

272 js[name] = self._FieldToJsonObject(field, field.default_value) 

273 

274 except ValueError as e: 

275 raise SerializeToJsonError( 

276 'Failed to serialize {0} field: {1}.'.format(field.name, e) 

277 ) from e 

278 

279 return js 

280 

281 def _FieldToJsonObject(self, field, value): 

282 """Converts field value according to Proto3 JSON Specification.""" 

283 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 

284 return self._MessageToJsonObject(value) 

285 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 

286 if self.use_integers_for_enums: 

287 return value 

288 if field.enum_type.full_name == 'google.protobuf.NullValue': 

289 return None 

290 enum_value = field.enum_type.values_by_number.get(value, None) 

291 if enum_value is not None: 

292 return enum_value.name 

293 else: 

294 if field.enum_type.is_closed: 

295 raise SerializeToJsonError( 

296 'Enum field contains an integer value ' 

297 'which can not mapped to an enum value.' 

298 ) 

299 else: 

300 return value 

301 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 

302 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: 

303 # Use base64 Data encoding for bytes 

304 return base64.b64encode(value).decode('utf-8') 

305 else: 

306 return str(value) 

307 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 

308 return bool(value) 

309 elif field.cpp_type in _INT64_TYPES: 

310 return str(value) 

311 elif field.cpp_type in _FLOAT_TYPES: 

312 if math.isinf(value): 

313 if value < 0.0: 

314 return _NEG_INFINITY 

315 else: 

316 return _INFINITY 

317 if math.isnan(value): 

318 return _NAN 

319 if self.float_format: 

320 return float(format(value, self.float_format)) 

321 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: 

322 return type_checkers.ToShortestFloat(value) 

323 

324 return value 

325 

326 def _AnyMessageToJsonObject(self, message): 

327 """Converts Any message according to Proto3 JSON Specification.""" 

328 if not message.ListFields(): 

329 return {} 

330 # Must print @type first, use OrderedDict instead of {} 

331 js = OrderedDict() 

332 type_url = message.type_url 

333 js['@type'] = type_url 

334 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

335 sub_message.ParseFromString(message.value) 

336 message_descriptor = sub_message.DESCRIPTOR 

337 full_name = message_descriptor.full_name 

338 if _IsWrapperMessage(message_descriptor): 

339 js['value'] = self._WrapperMessageToJsonObject(sub_message) 

340 return js 

341 if full_name in _WKTJSONMETHODS: 

342 js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0], sub_message)( 

343 self 

344 ) 

345 return js 

346 return self._RegularMessageToJsonObject(sub_message, js) 

347 

348 def _GenericMessageToJsonObject(self, message): 

349 """Converts message according to Proto3 JSON Specification.""" 

350 # Duration, Timestamp and FieldMask have ToJsonString method to do the 

351 # convert. Users can also call the method directly. 

352 return message.ToJsonString() 

353 

354 def _ValueMessageToJsonObject(self, message): 

355 """Converts Value message according to Proto3 JSON Specification.""" 

356 which = message.WhichOneof('kind') 

357 # If the Value message is not set treat as null_value when serialize 

358 # to JSON. The parse back result will be different from original message. 

359 if which is None or which == 'null_value': 

360 return None 

361 if which == 'list_value': 

362 return self._ListValueMessageToJsonObject(message.list_value) 

363 if which == 'number_value': 

364 value = message.number_value 

365 if math.isinf(value): 

366 raise ValueError( 

367 'Fail to serialize Infinity for Value.number_value, ' 

368 'which would parse as string_value' 

369 ) 

370 if math.isnan(value): 

371 raise ValueError( 

372 'Fail to serialize NaN for Value.number_value, ' 

373 'which would parse as string_value' 

374 ) 

375 else: 

376 value = getattr(message, which) 

377 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 

378 return self._FieldToJsonObject(oneof_descriptor, value) 

379 

380 def _ListValueMessageToJsonObject(self, message): 

381 """Converts ListValue message according to Proto3 JSON Specification.""" 

382 return [self._ValueMessageToJsonObject(value) for value in message.values] 

383 

384 def _StructMessageToJsonObject(self, message): 

385 """Converts Struct message according to Proto3 JSON Specification.""" 

386 fields = message.fields 

387 ret = {} 

388 for key in fields: 

389 ret[key] = self._ValueMessageToJsonObject(fields[key]) 

390 return ret 

391 

392 def _WrapperMessageToJsonObject(self, message): 

393 return self._FieldToJsonObject( 

394 message.DESCRIPTOR.fields_by_name['value'], message.value 

395 ) 

396 

397 

398def _IsWrapperMessage(message_descriptor): 

399 return message_descriptor.file.name == 'google/protobuf/wrappers.proto' 

400 

401 

402def _DuplicateChecker(js): 

403 result = {} 

404 for name, value in js: 

405 if name in result: 

406 raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name)) 

407 result[name] = value 

408 return result 

409 

410 

411def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 

412 """Creates a message from a type URL.""" 

413 db = symbol_database.Default() 

414 pool = db.pool if descriptor_pool is None else descriptor_pool 

415 type_name = type_url.split('/')[-1] 

416 try: 

417 message_descriptor = pool.FindMessageTypeByName(type_name) 

418 except KeyError as e: 

419 raise TypeError( 

420 'Can not find message descriptor by type_url: {0}'.format(type_url) 

421 ) from e 

422 message_class = message_factory.GetMessageClass(message_descriptor) 

423 return message_class() 

424 

425 

426def Parse( 

427 text, 

428 message, 

429 ignore_unknown_fields=False, 

430 descriptor_pool=None, 

431 max_recursion_depth=100, 

432): 

433 """Parses a JSON representation of a protocol message into a message. 

434 

435 Args: 

436 text: Message JSON representation. 

437 message: A protocol buffer message to merge into. 

438 ignore_unknown_fields: If True, do not raise errors for unknown fields. 

439 descriptor_pool: A Descriptor Pool for resolving types. If None use the 

440 default. 

441 max_recursion_depth: max recursion depth of JSON message to be deserialized. 

442 JSON messages over this depth will fail to be deserialized. Default value 

443 is 100. 

444 

445 Returns: 

446 The same message passed as argument. 

447 

448 Raises:: 

449 ParseError: On JSON parsing problems. 

450 """ 

451 if not isinstance(text, str): 

452 text = text.decode('utf-8') 

453 

454 try: 

455 js = json.loads(text, object_pairs_hook=_DuplicateChecker) 

456 except Exception as e: 

457 raise ParseError('Failed to load JSON: {0}.'.format(str(e))) from e 

458 

459 try: 

460 return ParseDict( 

461 js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

462 ) 

463 except ParseError as e: 

464 raise e 

465 except Exception as e: 

466 raise ParseError( 

467 'Failed to parse JSON: {0}: {1}.'.format(type(e).__name__, str(e)) 

468 ) from e 

469 

470 

471def ParseDict( 

472 js_dict, 

473 message, 

474 ignore_unknown_fields=False, 

475 descriptor_pool=None, 

476 max_recursion_depth=100, 

477): 

478 """Parses a JSON dictionary representation into a message. 

479 

480 Args: 

481 js_dict: Dict representation of a JSON message. 

482 message: A protocol buffer message to merge into. 

483 ignore_unknown_fields: If True, do not raise errors for unknown fields. 

484 descriptor_pool: A Descriptor Pool for resolving types. If None use the 

485 default. 

486 max_recursion_depth: max recursion depth of JSON message to be deserialized. 

487 JSON messages over this depth will fail to be deserialized. Default value 

488 is 100. 

489 

490 Returns: 

491 The same message passed as argument. 

492 """ 

493 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

494 parser.ConvertMessage(js_dict, message, '') 

495 return message 

496 

497 

498_INT_OR_FLOAT = (int, float) 

499_LIST_LIKE = (list, tuple) 

500 

501 

502class _Parser(object): 

503 """JSON format parser for protocol message.""" 

504 

505 def __init__( 

506 self, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

507 ): 

508 self.ignore_unknown_fields = ignore_unknown_fields 

509 self.descriptor_pool = descriptor_pool 

510 self.max_recursion_depth = max_recursion_depth 

511 self.recursion_depth = 0 

512 

513 def ConvertMessage(self, value, message, path): 

514 """Convert a JSON object into a message. 

515 

516 Args: 

517 value: A JSON object. 

518 message: A WKT or regular protocol message to record the data. 

519 path: parent path to log parse error info. 

520 

521 Raises: 

522 ParseError: In case of convert problems. 

523 """ 

524 self.recursion_depth += 1 

525 if self.recursion_depth > self.max_recursion_depth: 

526 raise ParseError( 

527 'Message too deep. Max recursion depth is {0}'.format( 

528 self.max_recursion_depth 

529 ) 

530 ) 

531 message_descriptor = message.DESCRIPTOR 

532 full_name = message_descriptor.full_name 

533 if not path: 

534 path = message_descriptor.name 

535 if _IsWrapperMessage(message_descriptor): 

536 self._ConvertWrapperMessage(value, message, path) 

537 elif full_name in _WKTJSONMETHODS: 

538 methodcaller(_WKTJSONMETHODS[full_name][1], value, message, path)(self) 

539 else: 

540 self._ConvertFieldValuePair(value, message, path) 

541 self.recursion_depth -= 1 

542 

543 def _ConvertFieldValuePair(self, js, message, path): 

544 """Convert field value pairs into regular message. 

545 

546 Args: 

547 js: A JSON object to convert the field value pairs. 

548 message: A regular protocol message to record the data. 

549 path: parent path to log parse error info. 

550 

551 Raises: 

552 ParseError: In case of problems converting. 

553 """ 

554 names = [] 

555 message_descriptor = message.DESCRIPTOR 

556 fields_by_json_name = dict( 

557 (f.json_name, f) for f in message_descriptor.fields 

558 ) 

559 for name in js: 

560 try: 

561 field = fields_by_json_name.get(name, None) 

562 if not field: 

563 field = message_descriptor.fields_by_name.get(name, None) 

564 if not field and _VALID_EXTENSION_NAME.match(name): 

565 if not message_descriptor.is_extendable: 

566 raise ParseError( 

567 'Message type {0} does not have extensions at {1}'.format( 

568 message_descriptor.full_name, path 

569 ) 

570 ) 

571 identifier = name[1:-1] # strip [] brackets 

572 # pylint: disable=protected-access 

573 field = message.Extensions._FindExtensionByName(identifier) 

574 # pylint: enable=protected-access 

575 if not field: 

576 # Try looking for extension by the message type name, dropping the 

577 # field name following the final . separator in full_name. 

578 identifier = '.'.join(identifier.split('.')[:-1]) 

579 # pylint: disable=protected-access 

580 field = message.Extensions._FindExtensionByName(identifier) 

581 # pylint: enable=protected-access 

582 if not field: 

583 if self.ignore_unknown_fields: 

584 continue 

585 raise ParseError( 

586 ( 

587 'Message type "{0}" has no field named "{1}" at "{2}".\n' 

588 ' Available Fields(except extensions): "{3}"' 

589 ).format( 

590 message_descriptor.full_name, 

591 name, 

592 path, 

593 [f.json_name for f in message_descriptor.fields], 

594 ) 

595 ) 

596 if name in names: 

597 raise ParseError( 

598 'Message type "{0}" should not have multiple ' 

599 '"{1}" fields at "{2}".'.format( 

600 message.DESCRIPTOR.full_name, name, path 

601 ) 

602 ) 

603 names.append(name) 

604 value = js[name] 

605 # Check no other oneof field is parsed. 

606 if field.containing_oneof is not None and value is not None: 

607 oneof_name = field.containing_oneof.name 

608 if oneof_name in names: 

609 raise ParseError( 

610 'Message type "{0}" should not have multiple ' 

611 '"{1}" oneof fields at "{2}".'.format( 

612 message.DESCRIPTOR.full_name, oneof_name, path 

613 ) 

614 ) 

615 names.append(oneof_name) 

616 

617 if value is None: 

618 if ( 

619 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

620 and field.message_type.full_name == 'google.protobuf.Value' 

621 ): 

622 sub_message = getattr(message, field.name) 

623 sub_message.null_value = 0 

624 elif ( 

625 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

626 and field.enum_type.full_name == 'google.protobuf.NullValue' 

627 ): 

628 setattr(message, field.name, 0) 

629 else: 

630 message.ClearField(field.name) 

631 continue 

632 

633 # Parse field value. 

634 if _IsMapEntry(field): 

635 message.ClearField(field.name) 

636 self._ConvertMapFieldValue( 

637 value, message, field, '{0}.{1}'.format(path, name) 

638 ) 

639 elif field.is_repeated: 

640 message.ClearField(field.name) 

641 if not isinstance(value, _LIST_LIKE): 

642 raise ParseError( 

643 'repeated field {0} must be in [] which is {1} at {2}'.format( 

644 name, value, path 

645 ) 

646 ) 

647 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 

648 # Repeated message field. 

649 for index, item in enumerate(value): 

650 sub_message = getattr(message, field.name).add() 

651 # None is a null_value in Value. 

652 if ( 

653 item is None 

654 and sub_message.DESCRIPTOR.full_name 

655 != 'google.protobuf.Value' 

656 ): 

657 raise ParseError( 

658 'null is not allowed to be used as an element' 

659 ' in a repeated field at {0}.{1}[{2}]'.format( 

660 path, name, index 

661 ) 

662 ) 

663 self.ConvertMessage( 

664 item, sub_message, '{0}.{1}[{2}]'.format(path, name, index) 

665 ) 

666 else: 

667 # Repeated scalar field. 

668 for index, item in enumerate(value): 

669 if item is None: 

670 raise ParseError( 

671 'null is not allowed to be used as an element' 

672 ' in a repeated field at {0}.{1}[{2}]'.format( 

673 path, name, index 

674 ) 

675 ) 

676 self._ConvertAndAppendScalar( 

677 message, field, item, '{0}.{1}[{2}]'.format(path, name, index) 

678 ) 

679 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 

680 if field.is_extension: 

681 sub_message = message.Extensions[field] 

682 else: 

683 sub_message = getattr(message, field.name) 

684 sub_message.SetInParent() 

685 self.ConvertMessage(value, sub_message, '{0}.{1}'.format(path, name)) 

686 else: 

687 if field.is_extension: 

688 self._ConvertAndSetScalarExtension( 

689 message, field, value, '{0}.{1}'.format(path, name) 

690 ) 

691 else: 

692 self._ConvertAndSetScalar( 

693 message, field, value, '{0}.{1}'.format(path, name) 

694 ) 

695 except ParseError as e: 

696 if field and field.containing_oneof is None: 

697 raise ParseError( 

698 'Failed to parse {0} field: {1}.'.format(name, e) 

699 ) from e 

700 else: 

701 raise ParseError(str(e)) from e 

702 except ValueError as e: 

703 raise ParseError( 

704 'Failed to parse {0} field: {1}.'.format(name, e) 

705 ) from e 

706 except TypeError as e: 

707 raise ParseError( 

708 'Failed to parse {0} field: {1}.'.format(name, e) 

709 ) from e 

710 

711 def _ConvertAnyMessage(self, value, message, path): 

712 """Convert a JSON representation into Any message.""" 

713 if isinstance(value, dict) and not value: 

714 return 

715 try: 

716 type_url = value['@type'] 

717 except KeyError as e: 

718 raise ParseError( 

719 '@type is missing when parsing any message at {0}'.format(path) 

720 ) from e 

721 

722 try: 

723 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

724 except TypeError as e: 

725 raise ParseError('{0} at {1}'.format(e, path)) from e 

726 message_descriptor = sub_message.DESCRIPTOR 

727 full_name = message_descriptor.full_name 

728 if _IsWrapperMessage(message_descriptor): 

729 self._ConvertWrapperMessage( 

730 value['value'], sub_message, '{0}.value'.format(path) 

731 ) 

732 elif full_name in _WKTJSONMETHODS: 

733 methodcaller( 

734 _WKTJSONMETHODS[full_name][1], 

735 value['value'], 

736 sub_message, 

737 '{0}.value'.format(path), 

738 )(self) 

739 else: 

740 del value['@type'] 

741 try: 

742 self._ConvertFieldValuePair(value, sub_message, path) 

743 finally: 

744 value['@type'] = type_url 

745 # Sets Any message 

746 message.value = sub_message.SerializeToString() 

747 message.type_url = type_url 

748 

749 def _ConvertGenericMessage(self, value, message, path): 

750 """Convert a JSON representation into message with FromJsonString.""" 

751 # Duration, Timestamp, FieldMask have a FromJsonString method to do the 

752 # conversion. Users can also call the method directly. 

753 try: 

754 message.FromJsonString(value) 

755 except ValueError as e: 

756 raise ParseError('{0} at {1}'.format(e, path)) from e 

757 

758 def _ConvertValueMessage(self, value, message, path): 

759 """Convert a JSON representation into Value message.""" 

760 if isinstance(value, dict): 

761 self._ConvertStructMessage(value, message.struct_value, path) 

762 elif isinstance(value, _LIST_LIKE): 

763 self._ConvertListOrTupleValueMessage(value, message.list_value, path) 

764 elif value is None: 

765 message.null_value = 0 

766 elif isinstance(value, bool): 

767 message.bool_value = value 

768 elif isinstance(value, str): 

769 message.string_value = value 

770 elif isinstance(value, _INT_OR_FLOAT): 

771 message.number_value = value 

772 else: 

773 raise ParseError( 

774 'Value {0} has unexpected type {1} at {2}'.format( 

775 value, type(value), path 

776 ) 

777 ) 

778 

779 def _ConvertListOrTupleValueMessage(self, value, message, path): 

780 """Convert a JSON representation into ListValue message.""" 

781 if not isinstance(value, _LIST_LIKE): 

782 raise ParseError( 

783 'ListValue must be in [] which is {0} at {1}'.format(value, path) 

784 ) 

785 message.ClearField('values') 

786 for index, item in enumerate(value): 

787 self._ConvertValueMessage( 

788 item, message.values.add(), '{0}[{1}]'.format(path, index) 

789 ) 

790 

791 def _ConvertStructMessage(self, value, message, path): 

792 """Convert a JSON representation into Struct message.""" 

793 if not isinstance(value, dict): 

794 raise ParseError( 

795 'Struct must be in a dict which is {0} at {1}'.format(value, path) 

796 ) 

797 # Clear will mark the struct as modified so it will be created even if 

798 # there are no values. 

799 message.Clear() 

800 for key in value: 

801 self._ConvertValueMessage( 

802 value[key], message.fields[key], '{0}.{1}'.format(path, key) 

803 ) 

804 return 

805 

806 def _ConvertWrapperMessage(self, value, message, path): 

807 """Convert a JSON representation into Wrapper message.""" 

808 field = message.DESCRIPTOR.fields_by_name['value'] 

809 self._ConvertAndSetScalar( 

810 message, field, value, path='{0}.value'.format(path) 

811 ) 

812 

813 def _ConvertMapFieldValue(self, value, message, field, path): 

814 """Convert map field value for a message map field. 

815 

816 Args: 

817 value: A JSON object to convert the map field value. 

818 message: A protocol message to record the converted data. 

819 field: The descriptor of the map field to be converted. 

820 path: parent path to log parse error info. 

821 

822 Raises: 

823 ParseError: In case of convert problems. 

824 """ 

825 if not isinstance(value, dict): 

826 raise ParseError( 

827 'Map field {0} must be in a dict which is {1} at {2}'.format( 

828 field.name, value, path 

829 ) 

830 ) 

831 key_field = field.message_type.fields_by_name['key'] 

832 value_field = field.message_type.fields_by_name['value'] 

833 for key in value: 

834 key_value = _ConvertScalarFieldValue( 

835 key, key_field, '{0}.key'.format(path), True 

836 ) 

837 if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 

838 self.ConvertMessage( 

839 value[key], 

840 getattr(message, field.name)[key_value], 

841 '{0}[{1}]'.format(path, key_value), 

842 ) 

843 else: 

844 self._ConvertAndSetScalarToMapKey( 

845 message, 

846 field, 

847 key_value, 

848 value[key], 

849 path='{0}[{1}]'.format(path, key_value), 

850 ) 

851 

852 def _ConvertAndSetScalarExtension( 

853 self, message, extension_field, js_value, path 

854 ): 

855 """Convert scalar from js_value and assign it to message.Extensions[extension_field].""" 

856 try: 

857 message.Extensions[extension_field] = _ConvertScalarFieldValue( 

858 js_value, extension_field, path 

859 ) 

860 except EnumStringValueParseError: 

861 if not self.ignore_unknown_fields: 

862 raise 

863 

864 def _ConvertAndSetScalar(self, message, field, js_value, path): 

865 """Convert scalar from js_value and assign it to message.field.""" 

866 try: 

867 setattr( 

868 message, field.name, _ConvertScalarFieldValue(js_value, field, path) 

869 ) 

870 except EnumStringValueParseError: 

871 if not self.ignore_unknown_fields: 

872 raise 

873 

874 def _ConvertAndAppendScalar(self, message, repeated_field, js_value, path): 

875 """Convert scalar from js_value and append it to message.repeated_field.""" 

876 try: 

877 getattr(message, repeated_field.name).append( 

878 _ConvertScalarFieldValue(js_value, repeated_field, path) 

879 ) 

880 except EnumStringValueParseError: 

881 if not self.ignore_unknown_fields: 

882 raise 

883 

884 def _ConvertAndSetScalarToMapKey( 

885 self, message, map_field, converted_key, js_value, path 

886 ): 

887 """Convert scalar from 'js_value' and add it to message.map_field[converted_key].""" 

888 try: 

889 getattr(message, map_field.name)[converted_key] = ( 

890 _ConvertScalarFieldValue( 

891 js_value, 

892 map_field.message_type.fields_by_name['value'], 

893 path, 

894 ) 

895 ) 

896 except EnumStringValueParseError: 

897 if not self.ignore_unknown_fields: 

898 raise 

899 

900 

901def _ConvertScalarFieldValue(value, field, path, require_str=False): 

902 """Convert a single scalar field value. 

903 

904 Args: 

905 value: A scalar value to convert the scalar field value. 

906 field: The descriptor of the field to convert. 

907 path: parent path to log parse error info. 

908 require_str: If True, the field value must be a str. 

909 

910 Returns: 

911 The converted scalar field value 

912 

913 Raises: 

914 ParseError: In case of convert problems. 

915 EnumStringValueParseError: In case of unknown enum string value. 

916 """ 

917 try: 

918 if field.cpp_type in _INT_TYPES: 

919 return _ConvertInteger(value) 

920 elif field.cpp_type in _FLOAT_TYPES: 

921 return _ConvertFloat(value, field) 

922 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 

923 return _ConvertBool(value, require_str) 

924 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 

925 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: 

926 if isinstance(value, str): 

927 encoded = value.encode('utf-8') 

928 else: 

929 encoded = value 

930 # Add extra padding '=' 

931 padded_value = encoded + b'=' * (4 - len(encoded) % 4) 

932 return base64.urlsafe_b64decode(padded_value) 

933 else: 

934 # Checking for unpaired surrogates appears to be unreliable, 

935 # depending on the specific Python version, so we check manually. 

936 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

937 raise ParseError('Unpaired surrogate') 

938 return value 

939 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 

940 # Convert an enum value. 

941 enum_value = field.enum_type.values_by_name.get(value, None) 

942 if enum_value is None: 

943 try: 

944 number = int(value) 

945 enum_value = field.enum_type.values_by_number.get(number, None) 

946 except ValueError as e: 

947 # Since parsing to integer failed and lookup in values_by_name didn't 

948 # find this name, we have an enum string value which is unknown. 

949 raise EnumStringValueParseError( 

950 'Invalid enum value {0} for enum type {1}'.format( 

951 value, field.enum_type.full_name 

952 ) 

953 ) from e 

954 if enum_value is None: 

955 if field.enum_type.is_closed: 

956 raise ParseError( 

957 'Invalid enum value {0} for enum type {1}'.format( 

958 value, field.enum_type.full_name 

959 ) 

960 ) 

961 else: 

962 return number 

963 return enum_value.number 

964 except EnumStringValueParseError as e: 

965 raise EnumStringValueParseError('{0} at {1}'.format(e, path)) from e 

966 except ParseError as e: 

967 raise ParseError('{0} at {1}'.format(e, path)) from e 

968 

969 

970def _ConvertInteger(value): 

971 """Convert an integer. 

972 

973 Args: 

974 value: A scalar value to convert. 

975 

976 Returns: 

977 The integer value. 

978 

979 Raises: 

980 ParseError: If an integer couldn't be consumed. 

981 """ 

982 if isinstance(value, float) and not value.is_integer(): 

983 raise ParseError("Couldn't parse integer: {0}".format(value)) 

984 

985 if isinstance(value, str) and value.find(' ') != -1: 

986 raise ParseError('Couldn\'t parse integer: "{0}"'.format(value)) 

987 

988 if isinstance(value, bool): 

989 raise ParseError( 

990 'Bool value {0} is not acceptable for integer field'.format(value) 

991 ) 

992 

993 try: 

994 return int(value) 

995 except ValueError as e: 

996 # Attempt to parse as an integer-valued float. 

997 try: 

998 f = float(value) 

999 except ValueError: 

1000 # Raise the original exception for the int parse. 

1001 raise e # pylint: disable=raise-missing-from 

1002 if not f.is_integer(): 

1003 raise ParseError( 

1004 'Couldn\'t parse non-integer string: "{0}"'.format(value) 

1005 ) from e 

1006 return int(f) 

1007 

1008 

1009def _ConvertFloat(value, field): 

1010 """Convert an floating point number.""" 

1011 if isinstance(value, float): 

1012 if math.isnan(value): 

1013 raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead') 

1014 if math.isinf(value): 

1015 if value > 0: 

1016 raise ParseError( 

1017 "Couldn't parse Infinity or value too large, " 

1018 'use quoted "Infinity" instead' 

1019 ) 

1020 else: 

1021 raise ParseError( 

1022 "Couldn't parse -Infinity or value too small, " 

1023 'use quoted "-Infinity" instead' 

1024 ) 

1025 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: 

1026 # pylint: disable=protected-access 

1027 if value > type_checkers._FLOAT_MAX: 

1028 raise ParseError('Float value too large') 

1029 # pylint: disable=protected-access 

1030 if value < type_checkers._FLOAT_MIN: 

1031 raise ParseError('Float value too small') 

1032 if value == 'nan': 

1033 raise ParseError('Couldn\'t parse float "nan", use "NaN" instead') 

1034 try: 

1035 # Assume Python compatible syntax. 

1036 return float(value) 

1037 except ValueError as e: 

1038 # Check alternative spellings. 

1039 if value == _NEG_INFINITY: 

1040 return float('-inf') 

1041 elif value == _INFINITY: 

1042 return float('inf') 

1043 elif value == _NAN: 

1044 return float('nan') 

1045 else: 

1046 raise ParseError("Couldn't parse float: {0}".format(value)) from e 

1047 

1048 

1049def _ConvertBool(value, require_str): 

1050 """Convert a boolean value. 

1051 

1052 Args: 

1053 value: A scalar value to convert. 

1054 require_str: If True, value must be a str. 

1055 

1056 Returns: 

1057 The bool parsed. 

1058 

1059 Raises: 

1060 ParseError: If a boolean value couldn't be consumed. 

1061 """ 

1062 if require_str: 

1063 if value == 'true': 

1064 return True 

1065 elif value == 'false': 

1066 return False 

1067 else: 

1068 raise ParseError('Expected "true" or "false", not {0}'.format(value)) 

1069 

1070 if not isinstance(value, bool): 

1071 raise ParseError('Expected true or false without quotes') 

1072 return value 

1073 

1074 

1075_WKTJSONMETHODS = { 

1076 'google.protobuf.Any': ['_AnyMessageToJsonObject', '_ConvertAnyMessage'], 

1077 'google.protobuf.Duration': [ 

1078 '_GenericMessageToJsonObject', 

1079 '_ConvertGenericMessage', 

1080 ], 

1081 'google.protobuf.FieldMask': [ 

1082 '_GenericMessageToJsonObject', 

1083 '_ConvertGenericMessage', 

1084 ], 

1085 'google.protobuf.ListValue': [ 

1086 '_ListValueMessageToJsonObject', 

1087 '_ConvertListOrTupleValueMessage', 

1088 ], 

1089 'google.protobuf.Struct': [ 

1090 '_StructMessageToJsonObject', 

1091 '_ConvertStructMessage', 

1092 ], 

1093 'google.protobuf.Timestamp': [ 

1094 '_GenericMessageToJsonObject', 

1095 '_ConvertGenericMessage', 

1096 ], 

1097 'google.protobuf.Value': [ 

1098 '_ValueMessageToJsonObject', 

1099 '_ConvertValueMessage', 

1100 ], 

1101}