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

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

487 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 ensure_ascii=True, 

88 always_print_fields_with_no_presence=False, 

89): 

90 """Converts protobuf message to JSON format. 

91 

92 Args: 

93 message: The protocol buffers message instance to serialize. 

94 always_print_fields_with_no_presence: If True, fields without presence 

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

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

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

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

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

100 lowerCamelCase. 

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

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

103 level is None, no newlines will be inserted. 

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

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

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

107 default. 

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

109 False, Unicode strings are returned unchanged. 

110 

111 Returns: 

112 A string containing the JSON formatted protocol buffer message. 

113 """ 

114 printer = _Printer( 

115 preserving_proto_field_name, 

116 use_integers_for_enums, 

117 descriptor_pool, 

118 always_print_fields_with_no_presence, 

119 ) 

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

121 

122 

123def MessageToDict( 

124 message, 

125 always_print_fields_with_no_presence=False, 

126 preserving_proto_field_name=False, 

127 use_integers_for_enums=False, 

128 descriptor_pool=None, 

129): 

130 """Converts protobuf message to a dictionary. 

131 

132 When the dictionary is encoded to JSON, it conforms to ProtoJSON spec. 

133 

134 Args: 

135 message: The protocol buffers message instance to serialize. 

136 always_print_fields_with_no_presence: If True, fields without presence 

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

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

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

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

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

142 lowerCamelCase. 

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

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

145 default. 

146 

147 Returns: 

148 A dict representation of the protocol buffer message. 

149 """ 

150 printer = _Printer( 

151 preserving_proto_field_name, 

152 use_integers_for_enums, 

153 descriptor_pool, 

154 always_print_fields_with_no_presence, 

155 ) 

156 # pylint: disable=protected-access 

157 return printer._MessageToJsonObject(message) 

158 

159 

160def _IsMapEntry(field): 

161 return ( 

162 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE 

163 and field.message_type.has_options 

164 and field.message_type.GetOptions().map_entry 

165 ) 

166 

167 

168class _Printer(object): 

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

170 

171 def __init__( 

172 self, 

173 preserving_proto_field_name=False, 

174 use_integers_for_enums=False, 

175 descriptor_pool=None, 

176 always_print_fields_with_no_presence=False, 

177 ): 

178 self.always_print_fields_with_no_presence = ( 

179 always_print_fields_with_no_presence 

180 ) 

181 self.preserving_proto_field_name = preserving_proto_field_name 

182 self.use_integers_for_enums = use_integers_for_enums 

183 self.descriptor_pool = descriptor_pool 

184 

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

186 js = self._MessageToJsonObject(message) 

187 return json.dumps( 

188 js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii 

189 ) 

190 

191 def _MessageToJsonObject(self, message): 

192 """Converts message to an object according to ProtoJSON Specification.""" 

193 message_descriptor = message.DESCRIPTOR 

194 full_name = message_descriptor.full_name 

195 if _IsWrapperMessage(message_descriptor): 

196 return self._WrapperMessageToJsonObject(message) 

197 if full_name in _WKTJSONMETHODS: 

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

199 js = {} 

200 return self._RegularMessageToJsonObject(message, js) 

201 

202 def _RegularMessageToJsonObject(self, message, js): 

203 """Converts normal message according to ProtoJSON Specification.""" 

204 fields = message.ListFields() 

205 

206 try: 

207 for field, value in fields: 

208 if field.is_extension: 

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

210 elif self.preserving_proto_field_name: 

211 name = field.name 

212 else: 

213 name = field.json_name 

214 

215 if _IsMapEntry(field): 

216 # Convert a map field. 

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

218 js_map = {} 

219 for key in value: 

220 if isinstance(key, bool): 

221 if key: 

222 recorded_key = 'true' 

223 else: 

224 recorded_key = 'false' 

225 else: 

226 recorded_key = str(key) 

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

228 js[name] = js_map 

229 elif field.is_repeated: 

230 # Convert a repeated field. 

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

232 else: 

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

234 

235 # Serialize default value if including_default_value_fields is True. 

236 if ( 

237 self.always_print_fields_with_no_presence 

238 ): 

239 message_descriptor = message.DESCRIPTOR 

240 for field in message_descriptor.fields: 

241 

242 # always_print_fields_with_no_presence doesn't apply to 

243 # any field which supports presence. 

244 if self.always_print_fields_with_no_presence and field.has_presence: 

245 continue 

246 

247 if self.preserving_proto_field_name: 

248 name = field.name 

249 else: 

250 name = field.json_name 

251 if name in js: 

252 # Skip the field which has been serialized already. 

253 continue 

254 if _IsMapEntry(field): 

255 js[name] = {} 

256 elif field.is_repeated: 

257 js[name] = [] 

258 else: 

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

260 

261 except ValueError as e: 

262 raise SerializeToJsonError( 

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

264 ) from e 

265 

266 return js 

267 

268 def _FieldToJsonObject(self, field, value): 

269 """Converts field value according to ProtoJSON Specification.""" 

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

271 return self._MessageToJsonObject(value) 

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

273 if self.use_integers_for_enums: 

274 return value 

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

276 return None 

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

278 if enum_value is not None: 

279 return enum_value.name 

280 else: 

281 if field.enum_type.is_closed: 

282 raise SerializeToJsonError( 

283 'Enum field contains an integer value ' 

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

285 ) 

286 else: 

287 return value 

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

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

290 # Use base64 Data encoding for bytes 

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

292 else: 

293 return str(value) 

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

295 return bool(value) 

296 elif field.cpp_type in _INT64_TYPES: 

297 return str(value) 

298 elif field.cpp_type in _FLOAT_TYPES: 

299 if math.isinf(value): 

300 if value < 0.0: 

301 return _NEG_INFINITY 

302 else: 

303 return _INFINITY 

304 if math.isnan(value): 

305 return _NAN 

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

307 return type_checkers.ToShortestFloat(value) 

308 

309 return value 

310 

311 def _AnyMessageToJsonObject(self, message): 

312 """Converts Any message according to ProtoJSON Specification.""" 

313 if not message.ListFields(): 

314 return {} 

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

316 js = OrderedDict() 

317 type_url = message.type_url 

318 js['@type'] = type_url 

319 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

320 sub_message.ParseFromString(message.value) 

321 message_descriptor = sub_message.DESCRIPTOR 

322 full_name = message_descriptor.full_name 

323 if _IsWrapperMessage(message_descriptor): 

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

325 return js 

326 if full_name in _WKTJSONMETHODS: 

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

328 self 

329 ) 

330 return js 

331 return self._RegularMessageToJsonObject(sub_message, js) 

332 

333 def _GenericMessageToJsonObject(self, message): 

334 """Converts message according to ProtoJSON Specification.""" 

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

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

337 return message.ToJsonString() 

338 

339 def _ValueMessageToJsonObject(self, message): 

340 """Converts Value message according to ProtoJSON Specification.""" 

341 which = message.WhichOneof('kind') 

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

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

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

345 return None 

346 if which == 'list_value': 

347 return self._ListValueMessageToJsonObject(message.list_value) 

348 if which == 'number_value': 

349 value = message.number_value 

350 if math.isinf(value): 

351 raise ValueError( 

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

353 'which would parse as string_value' 

354 ) 

355 if math.isnan(value): 

356 raise ValueError( 

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

358 'which would parse as string_value' 

359 ) 

360 else: 

361 value = getattr(message, which) 

362 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 

363 return self._FieldToJsonObject(oneof_descriptor, value) 

364 

365 def _ListValueMessageToJsonObject(self, message): 

366 """Converts ListValue message according to ProtoJSON Specification.""" 

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

368 

369 def _StructMessageToJsonObject(self, message): 

370 """Converts Struct message according to ProtoJSON Specification.""" 

371 fields = message.fields 

372 ret = {} 

373 for key in fields: 

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

375 return ret 

376 

377 def _WrapperMessageToJsonObject(self, message): 

378 return self._FieldToJsonObject( 

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

380 ) 

381 

382 

383def _IsWrapperMessage(message_descriptor): 

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

385 

386 

387def _DuplicateChecker(js): 

388 result = {} 

389 for name, value in js: 

390 if name in result: 

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

392 result[name] = value 

393 return result 

394 

395 

396def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 

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

398 db = symbol_database.Default() 

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

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

401 try: 

402 message_descriptor = pool.FindMessageTypeByName(type_name) 

403 except KeyError as e: 

404 raise TypeError( 

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

406 ) from e 

407 message_class = message_factory.GetMessageClass(message_descriptor) 

408 return message_class() 

409 

410 

411def Parse( 

412 text, 

413 message, 

414 ignore_unknown_fields=False, 

415 descriptor_pool=None, 

416 max_recursion_depth=100, 

417): 

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

419 

420 Args: 

421 text: Message JSON representation. 

422 message: A protocol buffer message to merge into. 

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

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

425 default. 

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

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

428 is 100. 

429 

430 Returns: 

431 The same message passed as argument. 

432 

433 Raises:: 

434 ParseError: On JSON parsing problems. 

435 """ 

436 if not isinstance(text, str): 

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

438 

439 try: 

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

441 except Exception as e: 

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

443 

444 try: 

445 return ParseDict( 

446 js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

447 ) 

448 except ParseError as e: 

449 raise e 

450 except Exception as e: 

451 raise ParseError( 

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

453 ) from e 

454 

455 

456def ParseDict( 

457 js_dict, 

458 message, 

459 ignore_unknown_fields=False, 

460 descriptor_pool=None, 

461 max_recursion_depth=100, 

462): 

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

464 

465 Args: 

466 js_dict: Dict representation of a JSON message. 

467 message: A protocol buffer message to merge into. 

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

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

470 default. 

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

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

473 is 100. 

474 

475 Returns: 

476 The same message passed as argument. 

477 """ 

478 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

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

480 return message 

481 

482 

483_INT_OR_FLOAT = (int, float) 

484_LIST_LIKE = (list, tuple) 

485 

486 

487class _Parser(object): 

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

489 

490 def __init__( 

491 self, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

492 ): 

493 self.ignore_unknown_fields = ignore_unknown_fields 

494 self.descriptor_pool = descriptor_pool 

495 self.max_recursion_depth = max_recursion_depth 

496 self.recursion_depth = 0 

497 

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

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

500 

501 Args: 

502 value: A JSON object. 

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

504 path: parent path to log parse error info. 

505 

506 Raises: 

507 ParseError: In case of convert problems. 

508 """ 

509 self.recursion_depth += 1 

510 if self.recursion_depth > self.max_recursion_depth: 

511 raise ParseError( 

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

513 self.max_recursion_depth 

514 ) 

515 ) 

516 message_descriptor = message.DESCRIPTOR 

517 full_name = message_descriptor.full_name 

518 if not path: 

519 path = message_descriptor.name 

520 if _IsWrapperMessage(message_descriptor): 

521 self._ConvertWrapperMessage(value, message, path) 

522 elif full_name in _WKTJSONMETHODS: 

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

524 else: 

525 self._ConvertFieldValuePair(value, message, path) 

526 self.recursion_depth -= 1 

527 

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

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

530 

531 Args: 

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

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

534 path: parent path to log parse error info. 

535 

536 Raises: 

537 ParseError: In case of problems converting. 

538 """ 

539 names = [] 

540 message_descriptor = message.DESCRIPTOR 

541 fields_by_json_name = dict( 

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

543 ) 

544 

545 def _ClearFieldOrExtension(message, field): 

546 if field.is_extension: 

547 message.ClearExtension(field) 

548 else: 

549 message.ClearField(field.name) 

550 

551 def _GetFieldOrExtension(message, field): 

552 if field.is_extension: 

553 return message.Extensions[field] 

554 else: 

555 return getattr(message, field.name) 

556 

557 def _SetFieldOrExtension(message, field, value): 

558 if field.is_extension: 

559 message.Extensions[field] = value 

560 else: 

561 setattr(message, field.name, value) 

562 

563 for name in js: 

564 try: 

565 field = fields_by_json_name.get(name, None) 

566 if not field: 

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

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

569 if not message_descriptor.is_extendable: 

570 raise ParseError( 

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

572 message_descriptor.full_name, path 

573 ) 

574 ) 

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

576 # pylint: disable=protected-access 

577 field = message.Extensions._FindExtensionByName(identifier) 

578 # pylint: enable=protected-access 

579 if not field: 

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

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

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

583 # pylint: disable=protected-access 

584 field = message.Extensions._FindExtensionByName(identifier) 

585 # pylint: enable=protected-access 

586 if not field: 

587 if self.ignore_unknown_fields: 

588 continue 

589 raise ParseError( 

590 ( 

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

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

593 ).format( 

594 message_descriptor.full_name, 

595 name, 

596 path, 

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

598 ) 

599 ) 

600 if name in names: 

601 raise ParseError( 

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

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

604 message.DESCRIPTOR.full_name, name, path 

605 ) 

606 ) 

607 names.append(name) 

608 value = js[name] 

609 # Check no other oneof field is parsed. 

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

611 oneof_name = field.containing_oneof.name 

612 if oneof_name in names: 

613 raise ParseError( 

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

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

616 message.DESCRIPTOR.full_name, oneof_name, path 

617 ) 

618 ) 

619 names.append(oneof_name) 

620 

621 if value is None: 

622 if ( 

623 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

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

625 ): 

626 sub_message = _GetFieldOrExtension(message, field) 

627 sub_message.null_value = 0 

628 elif ( 

629 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

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

631 ): 

632 _SetFieldOrExtension(message, field, 0) 

633 else: 

634 _ClearFieldOrExtension(message, field) 

635 continue 

636 

637 # Parse field value. 

638 if _IsMapEntry(field): 

639 _ClearFieldOrExtension(message, field) 

640 self._ConvertMapFieldValue( 

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

642 ) 

643 elif field.is_repeated: 

644 _ClearFieldOrExtension(message, field) 

645 if not isinstance(value, _LIST_LIKE): 

646 raise ParseError( 

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

648 name, value, path 

649 ) 

650 ) 

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

652 # Repeated message field. 

653 for index, item in enumerate(value): 

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

655 # None is a null_value in Value. 

656 if ( 

657 item is None 

658 and sub_message.DESCRIPTOR.full_name 

659 != 'google.protobuf.Value' 

660 ): 

661 raise ParseError( 

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

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

664 path, name, index 

665 ) 

666 ) 

667 self.ConvertMessage( 

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

669 ) 

670 else: 

671 # Repeated scalar field. 

672 for index, item in enumerate(value): 

673 if item is None: 

674 raise ParseError( 

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

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

677 path, name, index 

678 ) 

679 ) 

680 self._ConvertAndAppendScalar( 

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

682 ) 

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

684 sub_message = _GetFieldOrExtension(message, field) 

685 sub_message.SetInParent() 

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

687 else: 

688 self._ConvertAndSetScalar( 

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

690 ) 

691 except ParseError as e: 

692 if field and field.containing_oneof is None: 

693 raise ParseError( 

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

695 ) from e 

696 else: 

697 raise ParseError(str(e)) from e 

698 except ValueError as e: 

699 raise ParseError( 

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

701 ) from e 

702 except TypeError as e: 

703 raise ParseError( 

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

705 ) from e 

706 

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

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

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

710 return 

711 try: 

712 type_url = value['@type'] 

713 except KeyError as e: 

714 raise ParseError( 

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

716 ) from e 

717 

718 try: 

719 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

720 except TypeError as e: 

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

722 message_descriptor = sub_message.DESCRIPTOR 

723 full_name = message_descriptor.full_name 

724 if _IsWrapperMessage(message_descriptor): 

725 self._ConvertWrapperMessage( 

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

727 ) 

728 elif full_name in _WKTJSONMETHODS: 

729 methodcaller( 

730 _WKTJSONMETHODS[full_name][1], 

731 value['value'], 

732 sub_message, 

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

734 )(self) 

735 else: 

736 del value['@type'] 

737 try: 

738 self._ConvertFieldValuePair(value, sub_message, path) 

739 finally: 

740 value['@type'] = type_url 

741 # Sets Any message 

742 message.value = sub_message.SerializeToString() 

743 message.type_url = type_url 

744 

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

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

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

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

749 try: 

750 message.FromJsonString(value) 

751 except ValueError as e: 

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

753 

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

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

756 if isinstance(value, dict): 

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

758 elif isinstance(value, _LIST_LIKE): 

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

760 elif value is None: 

761 message.null_value = 0 

762 elif isinstance(value, bool): 

763 message.bool_value = value 

764 elif isinstance(value, str): 

765 message.string_value = value 

766 elif isinstance(value, _INT_OR_FLOAT): 

767 message.number_value = value 

768 else: 

769 raise ParseError( 

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

771 value, type(value), path 

772 ) 

773 ) 

774 

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

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

777 if not isinstance(value, _LIST_LIKE): 

778 raise ParseError( 

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

780 ) 

781 message.ClearField('values') 

782 for index, item in enumerate(value): 

783 self._ConvertValueMessage( 

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

785 ) 

786 

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

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

789 if not isinstance(value, dict): 

790 raise ParseError( 

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

792 ) 

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

794 # there are no values. 

795 message.Clear() 

796 for key in value: 

797 self._ConvertValueMessage( 

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

799 ) 

800 return 

801 

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

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

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

805 self._ConvertAndSetScalar( 

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

807 ) 

808 

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

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

811 

812 Args: 

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

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

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

816 path: parent path to log parse error info. 

817 

818 Raises: 

819 ParseError: In case of convert problems. 

820 """ 

821 if not isinstance(value, dict): 

822 raise ParseError( 

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

824 field.name, value, path 

825 ) 

826 ) 

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

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

829 for key in value: 

830 key_value = _ConvertScalarFieldValue( 

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

832 ) 

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

834 self.ConvertMessage( 

835 value[key], 

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

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

838 ) 

839 else: 

840 self._ConvertAndSetScalarToMapKey( 

841 message, 

842 field, 

843 key_value, 

844 value[key], 

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

846 ) 

847 

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

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

850 try: 

851 value = _ConvertScalarFieldValue(js_value, field, path) 

852 if field.is_extension: 

853 message.Extensions[field] = value 

854 else: 

855 setattr(message, field.name, value) 

856 except EnumStringValueParseError: 

857 if not self.ignore_unknown_fields: 

858 raise 

859 

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

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

862 try: 

863 if repeated_field.is_extension: 

864 repeated = message.Extensions[repeated_field] 

865 else: 

866 repeated = getattr(message, repeated_field.name) 

867 value = _ConvertScalarFieldValue(js_value, repeated_field, path) 

868 repeated.append(value) 

869 except EnumStringValueParseError: 

870 if not self.ignore_unknown_fields: 

871 raise 

872 

873 def _ConvertAndSetScalarToMapKey( 

874 self, message, map_field, converted_key, js_value, path 

875 ): 

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

877 try: 

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

879 _ConvertScalarFieldValue( 

880 js_value, 

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

882 path, 

883 ) 

884 ) 

885 except EnumStringValueParseError: 

886 if not self.ignore_unknown_fields: 

887 raise 

888 

889 

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

891 """Convert a single scalar field value. 

892 

893 Args: 

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

895 field: The descriptor of the field to convert. 

896 path: parent path to log parse error info. 

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

898 

899 Returns: 

900 The converted scalar field value 

901 

902 Raises: 

903 ParseError: In case of convert problems. 

904 EnumStringValueParseError: In case of unknown enum string value. 

905 """ 

906 try: 

907 if field.cpp_type in _INT_TYPES: 

908 return _ConvertInteger(value) 

909 elif field.cpp_type in _FLOAT_TYPES: 

910 return _ConvertFloat(value, field) 

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

912 return _ConvertBool(value, require_str) 

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

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

915 if isinstance(value, str): 

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

917 else: 

918 encoded = value 

919 # Add extra padding '=' 

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

921 return base64.urlsafe_b64decode(padded_value) 

922 else: 

923 # Checking for unpaired surrogates appears to be unreliable, 

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

925 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

926 raise ParseError('Unpaired surrogate') 

927 return value 

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

929 # Convert an enum value. 

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

931 if enum_value is None: 

932 try: 

933 number = int(value) 

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

935 except ValueError as e: 

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

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

938 raise EnumStringValueParseError( 

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

940 value, field.enum_type.full_name 

941 ) 

942 ) from e 

943 if enum_value is None: 

944 if field.enum_type.is_closed: 

945 raise ParseError( 

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

947 value, field.enum_type.full_name 

948 ) 

949 ) 

950 else: 

951 return number 

952 return enum_value.number 

953 except EnumStringValueParseError as e: 

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

955 except ParseError as e: 

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

957 

958 

959def _ConvertInteger(value): 

960 """Convert an integer. 

961 

962 Args: 

963 value: A scalar value to convert. 

964 

965 Returns: 

966 The integer value. 

967 

968 Raises: 

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

970 """ 

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

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

973 

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

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

976 

977 if isinstance(value, bool): 

978 raise ParseError( 

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

980 ) 

981 

982 try: 

983 return int(value) 

984 except ValueError as e: 

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

986 try: 

987 f = float(value) 

988 except ValueError: 

989 # Raise the original exception for the int parse. 

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

991 if not f.is_integer(): 

992 raise ParseError( 

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

994 ) from e 

995 return int(f) 

996 

997 

998def _ConvertFloat(value, field): 

999 """Convert an floating point number.""" 

1000 if isinstance(value, float): 

1001 if math.isnan(value): 

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

1003 if math.isinf(value): 

1004 if value > 0: 

1005 raise ParseError( 

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

1007 'use quoted "Infinity" instead' 

1008 ) 

1009 else: 

1010 raise ParseError( 

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

1012 'use quoted "-Infinity" instead' 

1013 ) 

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

1015 # pylint: disable=protected-access 

1016 if value > type_checkers._FLOAT_MAX: 

1017 raise ParseError('Float value too large') 

1018 # pylint: disable=protected-access 

1019 if value < type_checkers._FLOAT_MIN: 

1020 raise ParseError('Float value too small') 

1021 if value == 'nan': 

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

1023 try: 

1024 # Assume Python compatible syntax. 

1025 return float(value) 

1026 except ValueError as e: 

1027 # Check alternative spellings. 

1028 if value == _NEG_INFINITY: 

1029 return float('-inf') 

1030 elif value == _INFINITY: 

1031 return float('inf') 

1032 elif value == _NAN: 

1033 return float('nan') 

1034 else: 

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

1036 

1037 

1038def _ConvertBool(value, require_str): 

1039 """Convert a boolean value. 

1040 

1041 Args: 

1042 value: A scalar value to convert. 

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

1044 

1045 Returns: 

1046 The bool parsed. 

1047 

1048 Raises: 

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

1050 """ 

1051 if require_str: 

1052 if value == 'true': 

1053 return True 

1054 elif value == 'false': 

1055 return False 

1056 else: 

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

1058 

1059 if not isinstance(value, bool): 

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

1061 return value 

1062 

1063 

1064_WKTJSONMETHODS = { 

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

1066 'google.protobuf.Duration': [ 

1067 '_GenericMessageToJsonObject', 

1068 '_ConvertGenericMessage', 

1069 ], 

1070 'google.protobuf.FieldMask': [ 

1071 '_GenericMessageToJsonObject', 

1072 '_ConvertGenericMessage', 

1073 ], 

1074 'google.protobuf.ListValue': [ 

1075 '_ListValueMessageToJsonObject', 

1076 '_ConvertListOrTupleValueMessage', 

1077 ], 

1078 'google.protobuf.Struct': [ 

1079 '_StructMessageToJsonObject', 

1080 '_ConvertStructMessage', 

1081 ], 

1082 'google.protobuf.Timestamp': [ 

1083 '_GenericMessageToJsonObject', 

1084 '_ConvertGenericMessage', 

1085 ], 

1086 'google.protobuf.Value': [ 

1087 '_ValueMessageToJsonObject', 

1088 '_ConvertValueMessage', 

1089 ], 

1090}