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

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

486 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 self.preserving_proto_field_name: 

228 name = field.name 

229 else: 

230 name = field.json_name 

231 if _IsMapEntry(field): 

232 # Convert a map field. 

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

234 js_map = {} 

235 for key in value: 

236 if isinstance(key, bool): 

237 if key: 

238 recorded_key = 'true' 

239 else: 

240 recorded_key = 'false' 

241 else: 

242 recorded_key = str(key) 

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

244 js[name] = js_map 

245 elif field.is_repeated: 

246 # Convert a repeated field. 

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

248 elif field.is_extension: 

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

250 js[name] = self._FieldToJsonObject(field, 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 for name in js: 

566 try: 

567 field = fields_by_json_name.get(name, None) 

568 if not field: 

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

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

571 if not message_descriptor.is_extendable: 

572 raise ParseError( 

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

574 message_descriptor.full_name, path 

575 ) 

576 ) 

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

578 # pylint: disable=protected-access 

579 field = message.Extensions._FindExtensionByName(identifier) 

580 # pylint: enable=protected-access 

581 if not field: 

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

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

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

585 # pylint: disable=protected-access 

586 field = message.Extensions._FindExtensionByName(identifier) 

587 # pylint: enable=protected-access 

588 if not field: 

589 if self.ignore_unknown_fields: 

590 continue 

591 raise ParseError( 

592 ( 

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

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

595 ).format( 

596 message_descriptor.full_name, 

597 name, 

598 path, 

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

600 ) 

601 ) 

602 if name in names: 

603 raise ParseError( 

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

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

606 message.DESCRIPTOR.full_name, name, path 

607 ) 

608 ) 

609 names.append(name) 

610 value = js[name] 

611 # Check no other oneof field is parsed. 

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

613 oneof_name = field.containing_oneof.name 

614 if oneof_name in names: 

615 raise ParseError( 

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

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

618 message.DESCRIPTOR.full_name, oneof_name, path 

619 ) 

620 ) 

621 names.append(oneof_name) 

622 

623 if value is None: 

624 if ( 

625 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

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

627 ): 

628 sub_message = getattr(message, field.name) 

629 sub_message.null_value = 0 

630 elif ( 

631 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

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

633 ): 

634 setattr(message, field.name, 0) 

635 else: 

636 message.ClearField(field.name) 

637 continue 

638 

639 # Parse field value. 

640 if _IsMapEntry(field): 

641 message.ClearField(field.name) 

642 self._ConvertMapFieldValue( 

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

644 ) 

645 elif field.is_repeated: 

646 message.ClearField(field.name) 

647 if not isinstance(value, _LIST_LIKE): 

648 raise ParseError( 

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

650 name, value, path 

651 ) 

652 ) 

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

654 # Repeated message field. 

655 for index, item in enumerate(value): 

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

657 # None is a null_value in Value. 

658 if ( 

659 item is None 

660 and sub_message.DESCRIPTOR.full_name 

661 != 'google.protobuf.Value' 

662 ): 

663 raise ParseError( 

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

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

666 path, name, index 

667 ) 

668 ) 

669 self.ConvertMessage( 

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

671 ) 

672 else: 

673 # Repeated scalar field. 

674 for index, item in enumerate(value): 

675 if item is None: 

676 raise ParseError( 

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

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

679 path, name, index 

680 ) 

681 ) 

682 self._ConvertAndAppendScalar( 

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

684 ) 

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

686 if field.is_extension: 

687 sub_message = message.Extensions[field] 

688 else: 

689 sub_message = getattr(message, field.name) 

690 sub_message.SetInParent() 

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

692 else: 

693 if field.is_extension: 

694 self._ConvertAndSetScalarExtension( 

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

696 ) 

697 else: 

698 self._ConvertAndSetScalar( 

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

700 ) 

701 except ParseError as e: 

702 if field and field.containing_oneof is None: 

703 raise ParseError( 

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

705 ) from e 

706 else: 

707 raise ParseError(str(e)) from e 

708 except ValueError as e: 

709 raise ParseError( 

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

711 ) from e 

712 except TypeError as e: 

713 raise ParseError( 

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

715 ) from e 

716 

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

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

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

720 return 

721 try: 

722 type_url = value['@type'] 

723 except KeyError as e: 

724 raise ParseError( 

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

726 ) from e 

727 

728 try: 

729 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

730 except TypeError as e: 

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

732 message_descriptor = sub_message.DESCRIPTOR 

733 full_name = message_descriptor.full_name 

734 if _IsWrapperMessage(message_descriptor): 

735 self._ConvertWrapperMessage( 

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

737 ) 

738 elif full_name in _WKTJSONMETHODS: 

739 methodcaller( 

740 _WKTJSONMETHODS[full_name][1], 

741 value['value'], 

742 sub_message, 

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

744 )(self) 

745 else: 

746 del value['@type'] 

747 try: 

748 self._ConvertFieldValuePair(value, sub_message, path) 

749 finally: 

750 value['@type'] = type_url 

751 # Sets Any message 

752 message.value = sub_message.SerializeToString() 

753 message.type_url = type_url 

754 

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

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

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

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

759 try: 

760 message.FromJsonString(value) 

761 except ValueError as e: 

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

763 

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

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

766 if isinstance(value, dict): 

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

768 elif isinstance(value, _LIST_LIKE): 

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

770 elif value is None: 

771 message.null_value = 0 

772 elif isinstance(value, bool): 

773 message.bool_value = value 

774 elif isinstance(value, str): 

775 message.string_value = value 

776 elif isinstance(value, _INT_OR_FLOAT): 

777 message.number_value = value 

778 else: 

779 raise ParseError( 

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

781 value, type(value), path 

782 ) 

783 ) 

784 

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

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

787 if not isinstance(value, _LIST_LIKE): 

788 raise ParseError( 

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

790 ) 

791 message.ClearField('values') 

792 for index, item in enumerate(value): 

793 self._ConvertValueMessage( 

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

795 ) 

796 

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

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

799 if not isinstance(value, dict): 

800 raise ParseError( 

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

802 ) 

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

804 # there are no values. 

805 message.Clear() 

806 for key in value: 

807 self._ConvertValueMessage( 

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

809 ) 

810 return 

811 

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

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

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

815 self._ConvertAndSetScalar( 

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

817 ) 

818 

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

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

821 

822 Args: 

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

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

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

826 path: parent path to log parse error info. 

827 

828 Raises: 

829 ParseError: In case of convert problems. 

830 """ 

831 if not isinstance(value, dict): 

832 raise ParseError( 

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

834 field.name, value, path 

835 ) 

836 ) 

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

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

839 for key in value: 

840 key_value = _ConvertScalarFieldValue( 

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

842 ) 

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

844 self.ConvertMessage( 

845 value[key], 

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

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

848 ) 

849 else: 

850 self._ConvertAndSetScalarToMapKey( 

851 message, 

852 field, 

853 key_value, 

854 value[key], 

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

856 ) 

857 

858 def _ConvertAndSetScalarExtension( 

859 self, message, extension_field, js_value, path 

860 ): 

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

862 try: 

863 message.Extensions[extension_field] = _ConvertScalarFieldValue( 

864 js_value, extension_field, path 

865 ) 

866 except EnumStringValueParseError: 

867 if not self.ignore_unknown_fields: 

868 raise 

869 

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

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

872 try: 

873 setattr( 

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

875 ) 

876 except EnumStringValueParseError: 

877 if not self.ignore_unknown_fields: 

878 raise 

879 

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

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

882 try: 

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

884 _ConvertScalarFieldValue(js_value, repeated_field, path) 

885 ) 

886 except EnumStringValueParseError: 

887 if not self.ignore_unknown_fields: 

888 raise 

889 

890 def _ConvertAndSetScalarToMapKey( 

891 self, message, map_field, converted_key, js_value, path 

892 ): 

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

894 try: 

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

896 _ConvertScalarFieldValue( 

897 js_value, 

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

899 path, 

900 ) 

901 ) 

902 except EnumStringValueParseError: 

903 if not self.ignore_unknown_fields: 

904 raise 

905 

906 

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

908 """Convert a single scalar field value. 

909 

910 Args: 

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

912 field: The descriptor of the field to convert. 

913 path: parent path to log parse error info. 

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

915 

916 Returns: 

917 The converted scalar field value 

918 

919 Raises: 

920 ParseError: In case of convert problems. 

921 EnumStringValueParseError: In case of unknown enum string value. 

922 """ 

923 try: 

924 if field.cpp_type in _INT_TYPES: 

925 return _ConvertInteger(value) 

926 elif field.cpp_type in _FLOAT_TYPES: 

927 return _ConvertFloat(value, field) 

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

929 return _ConvertBool(value, require_str) 

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

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

932 if isinstance(value, str): 

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

934 else: 

935 encoded = value 

936 # Add extra padding '=' 

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

938 return base64.urlsafe_b64decode(padded_value) 

939 else: 

940 # Checking for unpaired surrogates appears to be unreliable, 

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

942 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

943 raise ParseError('Unpaired surrogate') 

944 return value 

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

946 # Convert an enum value. 

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

948 if enum_value is None: 

949 try: 

950 number = int(value) 

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

952 except ValueError as e: 

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

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

955 raise EnumStringValueParseError( 

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

957 value, field.enum_type.full_name 

958 ) 

959 ) from e 

960 if enum_value is None: 

961 if field.enum_type.is_closed: 

962 raise ParseError( 

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

964 value, field.enum_type.full_name 

965 ) 

966 ) 

967 else: 

968 return number 

969 return enum_value.number 

970 except EnumStringValueParseError as e: 

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

972 except ParseError as e: 

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

974 

975 

976def _ConvertInteger(value): 

977 """Convert an integer. 

978 

979 Args: 

980 value: A scalar value to convert. 

981 

982 Returns: 

983 The integer value. 

984 

985 Raises: 

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

987 """ 

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

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

990 

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

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

993 

994 if isinstance(value, bool): 

995 raise ParseError( 

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

997 ) 

998 

999 try: 

1000 return int(value) 

1001 except ValueError as e: 

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

1003 try: 

1004 f = float(value) 

1005 except ValueError: 

1006 # Raise the original exception for the int parse. 

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

1008 if not f.is_integer(): 

1009 raise ParseError( 

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

1011 ) from e 

1012 return int(f) 

1013 

1014 

1015def _ConvertFloat(value, field): 

1016 """Convert an floating point number.""" 

1017 if isinstance(value, float): 

1018 if math.isnan(value): 

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

1020 if math.isinf(value): 

1021 if value > 0: 

1022 raise ParseError( 

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

1024 'use quoted "Infinity" instead' 

1025 ) 

1026 else: 

1027 raise ParseError( 

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

1029 'use quoted "-Infinity" instead' 

1030 ) 

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

1032 # pylint: disable=protected-access 

1033 if value > type_checkers._FLOAT_MAX: 

1034 raise ParseError('Float value too large') 

1035 # pylint: disable=protected-access 

1036 if value < type_checkers._FLOAT_MIN: 

1037 raise ParseError('Float value too small') 

1038 if value == 'nan': 

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

1040 try: 

1041 # Assume Python compatible syntax. 

1042 return float(value) 

1043 except ValueError as e: 

1044 # Check alternative spellings. 

1045 if value == _NEG_INFINITY: 

1046 return float('-inf') 

1047 elif value == _INFINITY: 

1048 return float('inf') 

1049 elif value == _NAN: 

1050 return float('nan') 

1051 else: 

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

1053 

1054 

1055def _ConvertBool(value, require_str): 

1056 """Convert a boolean value. 

1057 

1058 Args: 

1059 value: A scalar value to convert. 

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

1061 

1062 Returns: 

1063 The bool parsed. 

1064 

1065 Raises: 

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

1067 """ 

1068 if require_str: 

1069 if value == 'true': 

1070 return True 

1071 elif value == 'false': 

1072 return False 

1073 else: 

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

1075 

1076 if not isinstance(value, bool): 

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

1078 return value 

1079 

1080 

1081_WKTJSONMETHODS = { 

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

1083 'google.protobuf.Duration': [ 

1084 '_GenericMessageToJsonObject', 

1085 '_ConvertGenericMessage', 

1086 ], 

1087 'google.protobuf.FieldMask': [ 

1088 '_GenericMessageToJsonObject', 

1089 '_ConvertGenericMessage', 

1090 ], 

1091 'google.protobuf.ListValue': [ 

1092 '_ListValueMessageToJsonObject', 

1093 '_ConvertListOrTupleValueMessage', 

1094 ], 

1095 'google.protobuf.Struct': [ 

1096 '_StructMessageToJsonObject', 

1097 '_ConvertStructMessage', 

1098 ], 

1099 'google.protobuf.Timestamp': [ 

1100 '_GenericMessageToJsonObject', 

1101 '_ConvertGenericMessage', 

1102 ], 

1103 'google.protobuf.Value': [ 

1104 '_ValueMessageToJsonObject', 

1105 '_ConvertValueMessage', 

1106 ], 

1107}