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

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

494 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 

29import warnings 

30 

31from google.protobuf import descriptor 

32from google.protobuf import message_factory 

33from google.protobuf import symbol_database 

34from google.protobuf.internal import type_checkers 

35 

36 

37_INT_TYPES = frozenset([ 

38 descriptor.FieldDescriptor.CPPTYPE_INT32, 

39 descriptor.FieldDescriptor.CPPTYPE_UINT32, 

40 descriptor.FieldDescriptor.CPPTYPE_INT64, 

41 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

42]) 

43_INT64_TYPES = frozenset([ 

44 descriptor.FieldDescriptor.CPPTYPE_INT64, 

45 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

46]) 

47_FLOAT_TYPES = frozenset([ 

48 descriptor.FieldDescriptor.CPPTYPE_FLOAT, 

49 descriptor.FieldDescriptor.CPPTYPE_DOUBLE, 

50]) 

51_INFINITY = 'Infinity' 

52_NEG_INFINITY = '-Infinity' 

53_NAN = 'NaN' 

54 

55_UNPAIRED_SURROGATE_PATTERN = re.compile( 

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

57) 

58 

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

60 

61 

62class Error(Exception): 

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

64 

65 

66class SerializeToJsonError(Error): 

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

68 

69 

70class ParseError(Error): 

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

72 

73 

74class EnumStringValueParseError(ParseError): 

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

76 

77 This exception is suppressed if ignore_unknown_fields is set. 

78 """ 

79 

80 

81def MessageToJson( 

82 message, 

83 preserving_proto_field_name=False, 

84 indent=2, 

85 sort_keys=False, 

86 use_integers_for_enums=False, 

87 descriptor_pool=None, 

88 float_precision=None, 

89 ensure_ascii=True, 

90 always_print_fields_with_no_presence=False, 

91): 

92 """Converts protobuf message to JSON format. 

93 

94 Args: 

95 message: The protocol buffers message instance to serialize. 

96 always_print_fields_with_no_presence: If True, fields without presence 

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

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

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

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

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

102 lowerCamelCase. 

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

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

105 level is None, no newlines will be inserted. 

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

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

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

109 default. 

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

111 digits. 

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

113 False, Unicode strings are returned unchanged. 

114 

115 Returns: 

116 A string containing the JSON formatted protocol buffer message. 

117 """ 

118 printer = _Printer( 

119 preserving_proto_field_name, 

120 use_integers_for_enums, 

121 descriptor_pool, 

122 float_precision, 

123 always_print_fields_with_no_presence, 

124 ) 

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

126 

127 

128def MessageToDict( 

129 message, 

130 always_print_fields_with_no_presence=False, 

131 preserving_proto_field_name=False, 

132 use_integers_for_enums=False, 

133 descriptor_pool=None, 

134 float_precision=None, 

135): 

136 """Converts protobuf message to a dictionary. 

137 

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

139 

140 Args: 

141 message: The protocol buffers message instance to serialize. 

142 always_print_fields_with_no_presence: If True, fields without presence 

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

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

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

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

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

148 lowerCamelCase. 

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

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

151 default. 

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

153 digits. 

154 

155 Returns: 

156 A dict representation of the protocol buffer message. 

157 """ 

158 printer = _Printer( 

159 preserving_proto_field_name, 

160 use_integers_for_enums, 

161 descriptor_pool, 

162 float_precision, 

163 always_print_fields_with_no_presence, 

164 ) 

165 # pylint: disable=protected-access 

166 return printer._MessageToJsonObject(message) 

167 

168 

169def _IsMapEntry(field): 

170 return ( 

171 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE 

172 and field.message_type.has_options 

173 and field.message_type.GetOptions().map_entry 

174 ) 

175 

176 

177class _Printer(object): 

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

179 

180 def __init__( 

181 self, 

182 preserving_proto_field_name=False, 

183 use_integers_for_enums=False, 

184 descriptor_pool=None, 

185 float_precision=None, 

186 always_print_fields_with_no_presence=False, 

187 ): 

188 self.always_print_fields_with_no_presence = ( 

189 always_print_fields_with_no_presence 

190 ) 

191 self.preserving_proto_field_name = preserving_proto_field_name 

192 self.use_integers_for_enums = use_integers_for_enums 

193 self.descriptor_pool = descriptor_pool 

194 if float_precision: 

195 warnings.warn( 

196 'float_precision option is deprecated for json_format. ' 

197 'This will turn into error in 7.34.0, please remove it ' 

198 'before that.' 

199 ) 

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

201 else: 

202 self.float_format = None 

203 

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

205 js = self._MessageToJsonObject(message) 

206 return json.dumps( 

207 js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii 

208 ) 

209 

210 def _MessageToJsonObject(self, message): 

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

212 message_descriptor = message.DESCRIPTOR 

213 full_name = message_descriptor.full_name 

214 if _IsWrapperMessage(message_descriptor): 

215 return self._WrapperMessageToJsonObject(message) 

216 if full_name in _WKTJSONMETHODS: 

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

218 js = {} 

219 return self._RegularMessageToJsonObject(message, js) 

220 

221 def _RegularMessageToJsonObject(self, message, js): 

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

223 fields = message.ListFields() 

224 

225 try: 

226 for field, value in fields: 

227 if field.is_extension: 

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

229 elif self.preserving_proto_field_name: 

230 name = field.name 

231 else: 

232 name = field.json_name 

233 

234 if _IsMapEntry(field): 

235 # Convert a map field. 

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

237 js_map = {} 

238 for key in value: 

239 if isinstance(key, bool): 

240 if key: 

241 recorded_key = 'true' 

242 else: 

243 recorded_key = 'false' 

244 else: 

245 recorded_key = str(key) 

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

247 js[name] = js_map 

248 elif field.is_repeated: 

249 # Convert a repeated field. 

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

251 else: 

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

253 

254 # Serialize default value if including_default_value_fields is True. 

255 if ( 

256 self.always_print_fields_with_no_presence 

257 ): 

258 message_descriptor = message.DESCRIPTOR 

259 for field in message_descriptor.fields: 

260 

261 # always_print_fields_with_no_presence doesn't apply to 

262 # any field which supports presence. 

263 if self.always_print_fields_with_no_presence and field.has_presence: 

264 continue 

265 

266 if self.preserving_proto_field_name: 

267 name = field.name 

268 else: 

269 name = field.json_name 

270 if name in js: 

271 # Skip the field which has been serialized already. 

272 continue 

273 if _IsMapEntry(field): 

274 js[name] = {} 

275 elif field.is_repeated: 

276 js[name] = [] 

277 else: 

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

279 

280 except ValueError as e: 

281 raise SerializeToJsonError( 

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

283 ) from e 

284 

285 return js 

286 

287 def _FieldToJsonObject(self, field, value): 

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

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

290 return self._MessageToJsonObject(value) 

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

292 if self.use_integers_for_enums: 

293 return value 

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

295 return None 

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

297 if enum_value is not None: 

298 return enum_value.name 

299 else: 

300 if field.enum_type.is_closed: 

301 raise SerializeToJsonError( 

302 'Enum field contains an integer value ' 

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

304 ) 

305 else: 

306 return value 

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

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

309 # Use base64 Data encoding for bytes 

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

311 else: 

312 return str(value) 

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

314 return bool(value) 

315 elif field.cpp_type in _INT64_TYPES: 

316 return str(value) 

317 elif field.cpp_type in _FLOAT_TYPES: 

318 if math.isinf(value): 

319 if value < 0.0: 

320 return _NEG_INFINITY 

321 else: 

322 return _INFINITY 

323 if math.isnan(value): 

324 return _NAN 

325 if self.float_format: 

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

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

328 return type_checkers.ToShortestFloat(value) 

329 

330 return value 

331 

332 def _AnyMessageToJsonObject(self, message): 

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

334 if not message.ListFields(): 

335 return {} 

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

337 js = OrderedDict() 

338 type_url = message.type_url 

339 js['@type'] = type_url 

340 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

341 sub_message.ParseFromString(message.value) 

342 message_descriptor = sub_message.DESCRIPTOR 

343 full_name = message_descriptor.full_name 

344 if _IsWrapperMessage(message_descriptor): 

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

346 return js 

347 if full_name in _WKTJSONMETHODS: 

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

349 self 

350 ) 

351 return js 

352 return self._RegularMessageToJsonObject(sub_message, js) 

353 

354 def _GenericMessageToJsonObject(self, message): 

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

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

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

358 return message.ToJsonString() 

359 

360 def _ValueMessageToJsonObject(self, message): 

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

362 which = message.WhichOneof('kind') 

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

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

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

366 return None 

367 if which == 'list_value': 

368 return self._ListValueMessageToJsonObject(message.list_value) 

369 if which == 'number_value': 

370 value = message.number_value 

371 if math.isinf(value): 

372 raise ValueError( 

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

374 'which would parse as string_value' 

375 ) 

376 if math.isnan(value): 

377 raise ValueError( 

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

379 'which would parse as string_value' 

380 ) 

381 else: 

382 value = getattr(message, which) 

383 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 

384 return self._FieldToJsonObject(oneof_descriptor, value) 

385 

386 def _ListValueMessageToJsonObject(self, message): 

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

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

389 

390 def _StructMessageToJsonObject(self, message): 

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

392 fields = message.fields 

393 ret = {} 

394 for key in fields: 

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

396 return ret 

397 

398 def _WrapperMessageToJsonObject(self, message): 

399 return self._FieldToJsonObject( 

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

401 ) 

402 

403 

404def _IsWrapperMessage(message_descriptor): 

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

406 

407 

408def _DuplicateChecker(js): 

409 result = {} 

410 for name, value in js: 

411 if name in result: 

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

413 result[name] = value 

414 return result 

415 

416 

417def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 

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

419 db = symbol_database.Default() 

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

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

422 try: 

423 message_descriptor = pool.FindMessageTypeByName(type_name) 

424 except KeyError as e: 

425 raise TypeError( 

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

427 ) from e 

428 message_class = message_factory.GetMessageClass(message_descriptor) 

429 return message_class() 

430 

431 

432def Parse( 

433 text, 

434 message, 

435 ignore_unknown_fields=False, 

436 descriptor_pool=None, 

437 max_recursion_depth=100, 

438): 

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

440 

441 Args: 

442 text: Message JSON representation. 

443 message: A protocol buffer message to merge into. 

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

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

446 default. 

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

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

449 is 100. 

450 

451 Returns: 

452 The same message passed as argument. 

453 

454 Raises:: 

455 ParseError: On JSON parsing problems. 

456 """ 

457 if not isinstance(text, str): 

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

459 

460 try: 

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

462 except Exception as e: 

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

464 

465 try: 

466 return ParseDict( 

467 js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

468 ) 

469 except ParseError as e: 

470 raise e 

471 except Exception as e: 

472 raise ParseError( 

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

474 ) from e 

475 

476 

477def ParseDict( 

478 js_dict, 

479 message, 

480 ignore_unknown_fields=False, 

481 descriptor_pool=None, 

482 max_recursion_depth=100, 

483): 

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

485 

486 Args: 

487 js_dict: Dict representation of a JSON message. 

488 message: A protocol buffer message to merge into. 

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

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

491 default. 

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

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

494 is 100. 

495 

496 Returns: 

497 The same message passed as argument. 

498 """ 

499 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

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

501 return message 

502 

503 

504_INT_OR_FLOAT = (int, float) 

505_LIST_LIKE = (list, tuple) 

506 

507 

508class _Parser(object): 

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

510 

511 def __init__( 

512 self, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

513 ): 

514 self.ignore_unknown_fields = ignore_unknown_fields 

515 self.descriptor_pool = descriptor_pool 

516 self.max_recursion_depth = max_recursion_depth 

517 self.recursion_depth = 0 

518 

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

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

521 

522 Args: 

523 value: A JSON object. 

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

525 path: parent path to log parse error info. 

526 

527 Raises: 

528 ParseError: In case of convert problems. 

529 """ 

530 self.recursion_depth += 1 

531 if self.recursion_depth > self.max_recursion_depth: 

532 raise ParseError( 

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

534 self.max_recursion_depth 

535 ) 

536 ) 

537 message_descriptor = message.DESCRIPTOR 

538 full_name = message_descriptor.full_name 

539 if not path: 

540 path = message_descriptor.name 

541 if _IsWrapperMessage(message_descriptor): 

542 self._ConvertWrapperMessage(value, message, path) 

543 elif full_name in _WKTJSONMETHODS: 

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

545 else: 

546 self._ConvertFieldValuePair(value, message, path) 

547 self.recursion_depth -= 1 

548 

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

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

551 

552 Args: 

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

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

555 path: parent path to log parse error info. 

556 

557 Raises: 

558 ParseError: In case of problems converting. 

559 """ 

560 names = [] 

561 message_descriptor = message.DESCRIPTOR 

562 fields_by_json_name = dict( 

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

564 ) 

565 

566 def _ClearFieldOrExtension(message, field): 

567 if field.is_extension: 

568 message.ClearExtension(field) 

569 else: 

570 message.ClearField(field.name) 

571 

572 def _GetFieldOrExtension(message, field): 

573 if field.is_extension: 

574 return message.Extensions[field] 

575 else: 

576 return getattr(message, field.name) 

577 

578 def _SetFieldOrExtension(message, field, value): 

579 if field.is_extension: 

580 message.Extensions[field] = value 

581 else: 

582 setattr(message, field.name, value) 

583 

584 for name in js: 

585 try: 

586 field = fields_by_json_name.get(name, None) 

587 if not field: 

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

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

590 if not message_descriptor.is_extendable: 

591 raise ParseError( 

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

593 message_descriptor.full_name, path 

594 ) 

595 ) 

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

597 # pylint: disable=protected-access 

598 field = message.Extensions._FindExtensionByName(identifier) 

599 # pylint: enable=protected-access 

600 if not field: 

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

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

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

604 # pylint: disable=protected-access 

605 field = message.Extensions._FindExtensionByName(identifier) 

606 # pylint: enable=protected-access 

607 if not field: 

608 if self.ignore_unknown_fields: 

609 continue 

610 raise ParseError( 

611 ( 

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

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

614 ).format( 

615 message_descriptor.full_name, 

616 name, 

617 path, 

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

619 ) 

620 ) 

621 if name in names: 

622 raise ParseError( 

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

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

625 message.DESCRIPTOR.full_name, name, path 

626 ) 

627 ) 

628 names.append(name) 

629 value = js[name] 

630 # Check no other oneof field is parsed. 

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

632 oneof_name = field.containing_oneof.name 

633 if oneof_name in names: 

634 raise ParseError( 

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

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

637 message.DESCRIPTOR.full_name, oneof_name, path 

638 ) 

639 ) 

640 names.append(oneof_name) 

641 

642 if value is None: 

643 if ( 

644 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

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

646 ): 

647 sub_message = _GetFieldOrExtension(message, field) 

648 sub_message.null_value = 0 

649 elif ( 

650 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

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

652 ): 

653 _SetFieldOrExtension(message, field, 0) 

654 else: 

655 _ClearFieldOrExtension(message, field) 

656 continue 

657 

658 # Parse field value. 

659 if _IsMapEntry(field): 

660 _ClearFieldOrExtension(message, field) 

661 self._ConvertMapFieldValue( 

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

663 ) 

664 elif field.is_repeated: 

665 _ClearFieldOrExtension(message, field) 

666 if not isinstance(value, _LIST_LIKE): 

667 raise ParseError( 

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

669 name, value, path 

670 ) 

671 ) 

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

673 # Repeated message field. 

674 for index, item in enumerate(value): 

675 sub_message = _GetFieldOrExtension(message, field).add() 

676 # None is a null_value in Value. 

677 if ( 

678 item is None 

679 and sub_message.DESCRIPTOR.full_name 

680 != 'google.protobuf.Value' 

681 ): 

682 raise ParseError( 

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

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

685 path, name, index 

686 ) 

687 ) 

688 self.ConvertMessage( 

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

690 ) 

691 else: 

692 # Repeated scalar field. 

693 for index, item in enumerate(value): 

694 if item is None: 

695 raise ParseError( 

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

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

698 path, name, index 

699 ) 

700 ) 

701 self._ConvertAndAppendScalar( 

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

703 ) 

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

705 sub_message = _GetFieldOrExtension(message, field) 

706 sub_message.SetInParent() 

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

708 else: 

709 self._ConvertAndSetScalar( 

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

711 ) 

712 except ParseError as e: 

713 if field and field.containing_oneof is None: 

714 raise ParseError( 

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

716 ) from e 

717 else: 

718 raise ParseError(str(e)) from e 

719 except ValueError as e: 

720 raise ParseError( 

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

722 ) from e 

723 except TypeError as e: 

724 raise ParseError( 

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

726 ) from e 

727 

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

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

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

731 return 

732 try: 

733 type_url = value['@type'] 

734 except KeyError as e: 

735 raise ParseError( 

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

737 ) from e 

738 

739 try: 

740 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

741 except TypeError as e: 

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

743 message_descriptor = sub_message.DESCRIPTOR 

744 full_name = message_descriptor.full_name 

745 if _IsWrapperMessage(message_descriptor): 

746 self._ConvertWrapperMessage( 

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

748 ) 

749 elif full_name in _WKTJSONMETHODS: 

750 methodcaller( 

751 _WKTJSONMETHODS[full_name][1], 

752 value['value'], 

753 sub_message, 

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

755 )(self) 

756 else: 

757 del value['@type'] 

758 try: 

759 self._ConvertFieldValuePair(value, sub_message, path) 

760 finally: 

761 value['@type'] = type_url 

762 # Sets Any message 

763 message.value = sub_message.SerializeToString() 

764 message.type_url = type_url 

765 

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

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

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

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

770 try: 

771 message.FromJsonString(value) 

772 except ValueError as e: 

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

774 

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

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

777 if isinstance(value, dict): 

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

779 elif isinstance(value, _LIST_LIKE): 

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

781 elif value is None: 

782 message.null_value = 0 

783 elif isinstance(value, bool): 

784 message.bool_value = value 

785 elif isinstance(value, str): 

786 message.string_value = value 

787 elif isinstance(value, _INT_OR_FLOAT): 

788 message.number_value = value 

789 else: 

790 raise ParseError( 

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

792 value, type(value), path 

793 ) 

794 ) 

795 

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

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

798 if not isinstance(value, _LIST_LIKE): 

799 raise ParseError( 

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

801 ) 

802 message.ClearField('values') 

803 for index, item in enumerate(value): 

804 self._ConvertValueMessage( 

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

806 ) 

807 

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

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

810 if not isinstance(value, dict): 

811 raise ParseError( 

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

813 ) 

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

815 # there are no values. 

816 message.Clear() 

817 for key in value: 

818 self._ConvertValueMessage( 

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

820 ) 

821 return 

822 

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

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

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

826 self._ConvertAndSetScalar( 

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

828 ) 

829 

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

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

832 

833 Args: 

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

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

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

837 path: parent path to log parse error info. 

838 

839 Raises: 

840 ParseError: In case of convert problems. 

841 """ 

842 if not isinstance(value, dict): 

843 raise ParseError( 

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

845 field.name, value, path 

846 ) 

847 ) 

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

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

850 for key in value: 

851 key_value = _ConvertScalarFieldValue( 

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

853 ) 

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

855 self.ConvertMessage( 

856 value[key], 

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

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

859 ) 

860 else: 

861 self._ConvertAndSetScalarToMapKey( 

862 message, 

863 field, 

864 key_value, 

865 value[key], 

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

867 ) 

868 

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

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

871 try: 

872 value = _ConvertScalarFieldValue(js_value, field, path) 

873 if field.is_extension: 

874 message.Extensions[field] = value 

875 else: 

876 setattr(message, field.name, value) 

877 except EnumStringValueParseError: 

878 if not self.ignore_unknown_fields: 

879 raise 

880 

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

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

883 try: 

884 if repeated_field.is_extension: 

885 repeated = message.Extensions[repeated_field] 

886 else: 

887 repeated = getattr(message, repeated_field.name) 

888 value = _ConvertScalarFieldValue(js_value, repeated_field, path) 

889 repeated.append(value) 

890 except EnumStringValueParseError: 

891 if not self.ignore_unknown_fields: 

892 raise 

893 

894 def _ConvertAndSetScalarToMapKey( 

895 self, message, map_field, converted_key, js_value, path 

896 ): 

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

898 try: 

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

900 _ConvertScalarFieldValue( 

901 js_value, 

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

903 path, 

904 ) 

905 ) 

906 except EnumStringValueParseError: 

907 if not self.ignore_unknown_fields: 

908 raise 

909 

910 

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

912 """Convert a single scalar field value. 

913 

914 Args: 

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

916 field: The descriptor of the field to convert. 

917 path: parent path to log parse error info. 

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

919 

920 Returns: 

921 The converted scalar field value 

922 

923 Raises: 

924 ParseError: In case of convert problems. 

925 EnumStringValueParseError: In case of unknown enum string value. 

926 """ 

927 try: 

928 if field.cpp_type in _INT_TYPES: 

929 return _ConvertInteger(value) 

930 elif field.cpp_type in _FLOAT_TYPES: 

931 return _ConvertFloat(value, field) 

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

933 return _ConvertBool(value, require_str) 

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

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

936 if isinstance(value, str): 

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

938 else: 

939 encoded = value 

940 # Add extra padding '=' 

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

942 return base64.urlsafe_b64decode(padded_value) 

943 else: 

944 # Checking for unpaired surrogates appears to be unreliable, 

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

946 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

947 raise ParseError('Unpaired surrogate') 

948 return value 

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

950 # Convert an enum value. 

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

952 if enum_value is None: 

953 try: 

954 number = int(value) 

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

956 except ValueError as e: 

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

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

959 raise EnumStringValueParseError( 

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

961 value, field.enum_type.full_name 

962 ) 

963 ) from e 

964 if enum_value is None: 

965 if field.enum_type.is_closed: 

966 raise ParseError( 

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

968 value, field.enum_type.full_name 

969 ) 

970 ) 

971 else: 

972 return number 

973 return enum_value.number 

974 except EnumStringValueParseError as e: 

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

976 except ParseError as e: 

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

978 

979 

980def _ConvertInteger(value): 

981 """Convert an integer. 

982 

983 Args: 

984 value: A scalar value to convert. 

985 

986 Returns: 

987 The integer value. 

988 

989 Raises: 

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

991 """ 

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

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

994 

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

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

997 

998 if isinstance(value, bool): 

999 raise ParseError( 

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

1001 ) 

1002 

1003 try: 

1004 return int(value) 

1005 except ValueError as e: 

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

1007 try: 

1008 f = float(value) 

1009 except ValueError: 

1010 # Raise the original exception for the int parse. 

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

1012 if not f.is_integer(): 

1013 raise ParseError( 

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

1015 ) from e 

1016 return int(f) 

1017 

1018 

1019def _ConvertFloat(value, field): 

1020 """Convert an floating point number.""" 

1021 if isinstance(value, float): 

1022 if math.isnan(value): 

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

1024 if math.isinf(value): 

1025 if value > 0: 

1026 raise ParseError( 

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

1028 'use quoted "Infinity" instead' 

1029 ) 

1030 else: 

1031 raise ParseError( 

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

1033 'use quoted "-Infinity" instead' 

1034 ) 

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

1036 # pylint: disable=protected-access 

1037 if value > type_checkers._FLOAT_MAX: 

1038 raise ParseError('Float value too large') 

1039 # pylint: disable=protected-access 

1040 if value < type_checkers._FLOAT_MIN: 

1041 raise ParseError('Float value too small') 

1042 if value == 'nan': 

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

1044 try: 

1045 # Assume Python compatible syntax. 

1046 return float(value) 

1047 except ValueError as e: 

1048 # Check alternative spellings. 

1049 if value == _NEG_INFINITY: 

1050 return float('-inf') 

1051 elif value == _INFINITY: 

1052 return float('inf') 

1053 elif value == _NAN: 

1054 return float('nan') 

1055 else: 

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

1057 

1058 

1059def _ConvertBool(value, require_str): 

1060 """Convert a boolean value. 

1061 

1062 Args: 

1063 value: A scalar value to convert. 

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

1065 

1066 Returns: 

1067 The bool parsed. 

1068 

1069 Raises: 

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

1071 """ 

1072 if require_str: 

1073 if value == 'true': 

1074 return True 

1075 elif value == 'false': 

1076 return False 

1077 else: 

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

1079 

1080 if not isinstance(value, bool): 

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

1082 return value 

1083 

1084 

1085_WKTJSONMETHODS = { 

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

1087 'google.protobuf.Duration': [ 

1088 '_GenericMessageToJsonObject', 

1089 '_ConvertGenericMessage', 

1090 ], 

1091 'google.protobuf.FieldMask': [ 

1092 '_GenericMessageToJsonObject', 

1093 '_ConvertGenericMessage', 

1094 ], 

1095 'google.protobuf.ListValue': [ 

1096 '_ListValueMessageToJsonObject', 

1097 '_ConvertListOrTupleValueMessage', 

1098 ], 

1099 'google.protobuf.Struct': [ 

1100 '_StructMessageToJsonObject', 

1101 '_ConvertStructMessage', 

1102 ], 

1103 'google.protobuf.Timestamp': [ 

1104 '_GenericMessageToJsonObject', 

1105 '_ConvertGenericMessage', 

1106 ], 

1107 'google.protobuf.Value': [ 

1108 '_ValueMessageToJsonObject', 

1109 '_ConvertValueMessage', 

1110 ], 

1111}