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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

484 statements  

1# Protocol Buffers - Google's data interchange format 

2# Copyright 2008 Google Inc. All rights reserved. 

3# 

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

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

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

7 

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

9 

10Simple usage example: 

11 

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

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

14 json_string = json_format.MessageToJson(message) 

15 

16 # Parse a json format string to proto object. 

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

18""" 

19 

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

21 

22 

23import base64 

24from collections import OrderedDict 

25import json 

26import math 

27from operator import methodcaller 

28import re 

29 

30from google.protobuf import descriptor 

31from google.protobuf import message_factory 

32from google.protobuf import symbol_database 

33from google.protobuf.internal import type_checkers 

34 

35 

36_INT_TYPES = frozenset([ 

37 descriptor.FieldDescriptor.CPPTYPE_INT32, 

38 descriptor.FieldDescriptor.CPPTYPE_UINT32, 

39 descriptor.FieldDescriptor.CPPTYPE_INT64, 

40 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

41]) 

42_INT64_TYPES = frozenset([ 

43 descriptor.FieldDescriptor.CPPTYPE_INT64, 

44 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

45]) 

46_FLOAT_TYPES = frozenset([ 

47 descriptor.FieldDescriptor.CPPTYPE_FLOAT, 

48 descriptor.FieldDescriptor.CPPTYPE_DOUBLE, 

49]) 

50_INFINITY = 'Infinity' 

51_NEG_INFINITY = '-Infinity' 

52_NAN = 'NaN' 

53 

54_UNPAIRED_SURROGATE_PATTERN = re.compile( 

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

56) 

57 

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

59 

60 

61class Error(Exception): 

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

63 

64 

65class SerializeToJsonError(Error): 

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

67 

68 

69class ParseError(Error): 

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

71 

72 

73class EnumStringValueParseError(ParseError): 

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

75 This exception is suppressed if ignore_unknown_fields is set. 

76 """ 

77 

78 

79def MessageToJson( 

80 message, 

81 preserving_proto_field_name=False, 

82 indent=2, 

83 sort_keys=False, 

84 use_integers_for_enums=False, 

85 descriptor_pool=None, 

86 float_precision=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 

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

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

97 this 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 float_precision: If set, use this to specify float field valid digits. 

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

110 False, Unicode strings are returned unchanged. 

111 

112 Returns: 

113 A string containing the JSON formatted protocol buffer message. 

114 """ 

115 printer = _Printer( 

116 preserving_proto_field_name, 

117 use_integers_for_enums, 

118 descriptor_pool, 

119 float_precision, 

120 always_print_fields_with_no_presence 

121 ) 

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

123 

124 

125def MessageToDict( 

126 message, 

127 always_print_fields_with_no_presence=False, 

128 preserving_proto_field_name=False, 

129 use_integers_for_enums=False, 

130 descriptor_pool=None, 

131 float_precision=None, 

132): 

133 """Converts protobuf message to a dictionary. 

134 

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

136 

137 Args: 

138 message: The protocol buffers message instance to serialize. 

139 always_print_fields_with_no_presence: If True, fields without 

140 presence (implicit presence scalars, repeated fields, and map fields) will 

141 always be serialized. Any field that supports presence is not affected by 

142 this option (including singular message fields and oneof fields). 

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

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

145 lowerCamelCase. 

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

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

148 default. 

149 float_precision: If set, use this to specify float field valid digits. 

150 

151 Returns: 

152 A dict representation of the protocol buffer message. 

153 """ 

154 printer = _Printer( 

155 preserving_proto_field_name, 

156 use_integers_for_enums, 

157 descriptor_pool, 

158 float_precision, 

159 always_print_fields_with_no_presence, 

160 ) 

161 # pylint: disable=protected-access 

162 return printer._MessageToJsonObject(message) 

163 

164 

165def _IsMapEntry(field): 

166 return ( 

167 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE 

168 and field.message_type.has_options 

169 and field.message_type.GetOptions().map_entry 

170 ) 

171 

172 

173class _Printer(object): 

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

175 

176 def __init__( 

177 self, 

178 preserving_proto_field_name=False, 

179 use_integers_for_enums=False, 

180 descriptor_pool=None, 

181 float_precision=None, 

182 always_print_fields_with_no_presence=False, 

183 ): 

184 self.always_print_fields_with_no_presence = ( 

185 always_print_fields_with_no_presence 

186 ) 

187 self.preserving_proto_field_name = preserving_proto_field_name 

188 self.use_integers_for_enums = use_integers_for_enums 

189 self.descriptor_pool = descriptor_pool 

190 if float_precision: 

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

192 else: 

193 self.float_format = None 

194 

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

196 js = self._MessageToJsonObject(message) 

197 return json.dumps( 

198 js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii 

199 ) 

200 

201 def _MessageToJsonObject(self, message): 

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

203 message_descriptor = message.DESCRIPTOR 

204 full_name = message_descriptor.full_name 

205 if _IsWrapperMessage(message_descriptor): 

206 return self._WrapperMessageToJsonObject(message) 

207 if full_name in _WKTJSONMETHODS: 

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

209 js = {} 

210 return self._RegularMessageToJsonObject(message, js) 

211 

212 def _RegularMessageToJsonObject(self, message, js): 

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

214 fields = message.ListFields() 

215 

216 try: 

217 for field, value in fields: 

218 if self.preserving_proto_field_name: 

219 name = field.name 

220 else: 

221 name = field.json_name 

222 if _IsMapEntry(field): 

223 # Convert a map field. 

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

225 js_map = {} 

226 for key in value: 

227 if isinstance(key, bool): 

228 if key: 

229 recorded_key = 'true' 

230 else: 

231 recorded_key = 'false' 

232 else: 

233 recorded_key = str(key) 

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

235 js[name] = js_map 

236 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 

237 # Convert a repeated field. 

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

239 elif field.is_extension: 

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

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

242 else: 

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

244 

245 # Serialize default value if including_default_value_fields is True. 

246 if ( 

247 self.always_print_fields_with_no_presence 

248 ): 

249 message_descriptor = message.DESCRIPTOR 

250 for field in message_descriptor.fields: 

251 

252 # always_print_fields_with_no_presence doesn't apply to 

253 # any field which supports presence. 

254 if ( 

255 self.always_print_fields_with_no_presence 

256 and field.has_presence 

257 ): 

258 continue 

259 

260 if self.preserving_proto_field_name: 

261 name = field.name 

262 else: 

263 name = field.json_name 

264 if name in js: 

265 # Skip the field which has been serialized already. 

266 continue 

267 if _IsMapEntry(field): 

268 js[name] = {} 

269 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 

270 js[name] = [] 

271 else: 

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

273 

274 except ValueError as e: 

275 raise SerializeToJsonError( 

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

277 ) from e 

278 

279 return js 

280 

281 def _FieldToJsonObject(self, field, value): 

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

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

284 return self._MessageToJsonObject(value) 

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

286 if self.use_integers_for_enums: 

287 return value 

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

289 return None 

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

291 if enum_value is not None: 

292 return enum_value.name 

293 else: 

294 if field.enum_type.is_closed: 

295 raise SerializeToJsonError( 

296 'Enum field contains an integer value ' 

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

298 ) 

299 else: 

300 return value 

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

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

303 # Use base64 Data encoding for bytes 

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

305 else: 

306 return str(value) 

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

308 return bool(value) 

309 elif field.cpp_type in _INT64_TYPES: 

310 return str(value) 

311 elif field.cpp_type in _FLOAT_TYPES: 

312 if math.isinf(value): 

313 if value < 0.0: 

314 return _NEG_INFINITY 

315 else: 

316 return _INFINITY 

317 if math.isnan(value): 

318 return _NAN 

319 if self.float_format: 

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

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

322 return type_checkers.ToShortestFloat(value) 

323 

324 return value 

325 

326 def _AnyMessageToJsonObject(self, message): 

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

328 if not message.ListFields(): 

329 return {} 

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

331 js = OrderedDict() 

332 type_url = message.type_url 

333 js['@type'] = type_url 

334 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

335 sub_message.ParseFromString(message.value) 

336 message_descriptor = sub_message.DESCRIPTOR 

337 full_name = message_descriptor.full_name 

338 if _IsWrapperMessage(message_descriptor): 

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

340 return js 

341 if full_name in _WKTJSONMETHODS: 

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

343 self 

344 ) 

345 return js 

346 return self._RegularMessageToJsonObject(sub_message, js) 

347 

348 def _GenericMessageToJsonObject(self, message): 

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

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

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

352 return message.ToJsonString() 

353 

354 def _ValueMessageToJsonObject(self, message): 

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

356 which = message.WhichOneof('kind') 

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

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

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

360 return None 

361 if which == 'list_value': 

362 return self._ListValueMessageToJsonObject(message.list_value) 

363 if which == 'number_value': 

364 value = message.number_value 

365 if math.isinf(value): 

366 raise ValueError( 

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

368 'which would parse as string_value' 

369 ) 

370 if math.isnan(value): 

371 raise ValueError( 

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

373 'which would parse as string_value' 

374 ) 

375 else: 

376 value = getattr(message, which) 

377 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 

378 return self._FieldToJsonObject(oneof_descriptor, value) 

379 

380 def _ListValueMessageToJsonObject(self, message): 

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

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

383 

384 def _StructMessageToJsonObject(self, message): 

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

386 fields = message.fields 

387 ret = {} 

388 for key in fields: 

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

390 return ret 

391 

392 def _WrapperMessageToJsonObject(self, message): 

393 return self._FieldToJsonObject( 

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

395 ) 

396 

397 

398def _IsWrapperMessage(message_descriptor): 

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

400 

401 

402def _DuplicateChecker(js): 

403 result = {} 

404 for name, value in js: 

405 if name in result: 

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

407 result[name] = value 

408 return result 

409 

410 

411def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 

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

413 db = symbol_database.Default() 

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

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

416 try: 

417 message_descriptor = pool.FindMessageTypeByName(type_name) 

418 except KeyError as e: 

419 raise TypeError( 

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

421 ) from e 

422 message_class = message_factory.GetMessageClass(message_descriptor) 

423 return message_class() 

424 

425 

426def Parse( 

427 text, 

428 message, 

429 ignore_unknown_fields=False, 

430 descriptor_pool=None, 

431 max_recursion_depth=100, 

432): 

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

434 

435 Args: 

436 text: Message JSON representation. 

437 message: A protocol buffer message to merge into. 

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

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

440 default. 

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

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

443 is 100. 

444 

445 Returns: 

446 The same message passed as argument. 

447 

448 Raises:: 

449 ParseError: On JSON parsing problems. 

450 """ 

451 if not isinstance(text, str): 

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

453 

454 try: 

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

456 except Exception as e: 

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

458 

459 try: 

460 return ParseDict( 

461 js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

462 ) 

463 except ParseError as e: 

464 raise e 

465 except Exception as e: 

466 raise ParseError( 

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

468 ) from e 

469 

470 

471def ParseDict( 

472 js_dict, 

473 message, 

474 ignore_unknown_fields=False, 

475 descriptor_pool=None, 

476 max_recursion_depth=100, 

477): 

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

479 

480 Args: 

481 js_dict: Dict representation of a JSON message. 

482 message: A protocol buffer message to merge into. 

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

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

485 default. 

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

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

488 is 100. 

489 

490 Returns: 

491 The same message passed as argument. 

492 """ 

493 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

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

495 return message 

496 

497 

498_INT_OR_FLOAT = (int, float) 

499_LIST_LIKE = (list, tuple) 

500 

501 

502class _Parser(object): 

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

504 

505 def __init__( 

506 self, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

507 ): 

508 self.ignore_unknown_fields = ignore_unknown_fields 

509 self.descriptor_pool = descriptor_pool 

510 self.max_recursion_depth = max_recursion_depth 

511 self.recursion_depth = 0 

512 

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

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

515 

516 Args: 

517 value: A JSON object. 

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

519 path: parent path to log parse error info. 

520 

521 Raises: 

522 ParseError: In case of convert problems. 

523 """ 

524 self.recursion_depth += 1 

525 if self.recursion_depth > self.max_recursion_depth: 

526 raise ParseError( 

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

528 self.max_recursion_depth 

529 ) 

530 ) 

531 message_descriptor = message.DESCRIPTOR 

532 full_name = message_descriptor.full_name 

533 if not path: 

534 path = message_descriptor.name 

535 if _IsWrapperMessage(message_descriptor): 

536 self._ConvertWrapperMessage(value, message, path) 

537 elif full_name in _WKTJSONMETHODS: 

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

539 else: 

540 self._ConvertFieldValuePair(value, message, path) 

541 self.recursion_depth -= 1 

542 

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

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

545 

546 Args: 

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

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

549 path: parent path to log parse error info. 

550 

551 Raises: 

552 ParseError: In case of problems converting. 

553 """ 

554 names = [] 

555 message_descriptor = message.DESCRIPTOR 

556 fields_by_json_name = dict( 

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

558 ) 

559 for name in js: 

560 try: 

561 field = fields_by_json_name.get(name, None) 

562 if not field: 

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

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

565 if not message_descriptor.is_extendable: 

566 raise ParseError( 

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

568 message_descriptor.full_name, path 

569 ) 

570 ) 

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

572 # pylint: disable=protected-access 

573 field = message.Extensions._FindExtensionByName(identifier) 

574 # pylint: enable=protected-access 

575 if not field: 

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

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

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

579 # pylint: disable=protected-access 

580 field = message.Extensions._FindExtensionByName(identifier) 

581 # pylint: enable=protected-access 

582 if not field: 

583 if self.ignore_unknown_fields: 

584 continue 

585 raise ParseError( 

586 ( 

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

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

589 ).format( 

590 message_descriptor.full_name, 

591 name, 

592 path, 

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

594 ) 

595 ) 

596 if name in names: 

597 raise ParseError( 

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

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

600 message.DESCRIPTOR.full_name, name, path 

601 ) 

602 ) 

603 names.append(name) 

604 value = js[name] 

605 # Check no other oneof field is parsed. 

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

607 oneof_name = field.containing_oneof.name 

608 if oneof_name in names: 

609 raise ParseError( 

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

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

612 message.DESCRIPTOR.full_name, oneof_name, path 

613 ) 

614 ) 

615 names.append(oneof_name) 

616 

617 if value is None: 

618 if ( 

619 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

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

621 ): 

622 sub_message = getattr(message, field.name) 

623 sub_message.null_value = 0 

624 elif ( 

625 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

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

627 ): 

628 setattr(message, field.name, 0) 

629 else: 

630 message.ClearField(field.name) 

631 continue 

632 

633 # Parse field value. 

634 if _IsMapEntry(field): 

635 message.ClearField(field.name) 

636 self._ConvertMapFieldValue( 

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

638 ) 

639 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 

640 message.ClearField(field.name) 

641 if not isinstance(value, _LIST_LIKE): 

642 raise ParseError( 

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

644 name, value, path 

645 ) 

646 ) 

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

648 # Repeated message field. 

649 for index, item in enumerate(value): 

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

651 # None is a null_value in Value. 

652 if ( 

653 item is None 

654 and sub_message.DESCRIPTOR.full_name 

655 != 'google.protobuf.Value' 

656 ): 

657 raise ParseError( 

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

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

660 path, name, index 

661 ) 

662 ) 

663 self.ConvertMessage( 

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

665 ) 

666 else: 

667 # Repeated scalar field. 

668 for index, item in enumerate(value): 

669 if item is None: 

670 raise ParseError( 

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

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

673 path, name, index 

674 ) 

675 ) 

676 self._ConvertAndAppendScalar( 

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

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

679 if field.is_extension: 

680 sub_message = message.Extensions[field] 

681 else: 

682 sub_message = getattr(message, field.name) 

683 sub_message.SetInParent() 

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

685 else: 

686 if field.is_extension: 

687 self._ConvertAndSetScalarExtension(message, field, value, '{0}.{1}'.format(path, name)) 

688 else: 

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

690 except ParseError as e: 

691 if field and field.containing_oneof is None: 

692 raise ParseError( 

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

694 ) from e 

695 else: 

696 raise ParseError(str(e)) from e 

697 except ValueError as e: 

698 raise ParseError( 

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

700 ) from e 

701 except TypeError as e: 

702 raise ParseError( 

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

704 ) from e 

705 

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

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

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

709 return 

710 try: 

711 type_url = value['@type'] 

712 except KeyError as e: 

713 raise ParseError( 

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

715 ) from e 

716 

717 try: 

718 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

719 except TypeError as e: 

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

721 message_descriptor = sub_message.DESCRIPTOR 

722 full_name = message_descriptor.full_name 

723 if _IsWrapperMessage(message_descriptor): 

724 self._ConvertWrapperMessage( 

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

726 ) 

727 elif full_name in _WKTJSONMETHODS: 

728 methodcaller( 

729 _WKTJSONMETHODS[full_name][1], 

730 value['value'], 

731 sub_message, 

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

733 )(self) 

734 else: 

735 del value['@type'] 

736 try: 

737 self._ConvertFieldValuePair(value, sub_message, path) 

738 finally: 

739 value['@type'] = type_url 

740 # Sets Any message 

741 message.value = sub_message.SerializeToString() 

742 message.type_url = type_url 

743 

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

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

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

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

748 try: 

749 message.FromJsonString(value) 

750 except ValueError as e: 

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

752 

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

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

755 if isinstance(value, dict): 

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

757 elif isinstance(value, _LIST_LIKE): 

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

759 elif value is None: 

760 message.null_value = 0 

761 elif isinstance(value, bool): 

762 message.bool_value = value 

763 elif isinstance(value, str): 

764 message.string_value = value 

765 elif isinstance(value, _INT_OR_FLOAT): 

766 message.number_value = value 

767 else: 

768 raise ParseError( 

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

770 value, type(value), path 

771 ) 

772 ) 

773 

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

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

776 if not isinstance(value, _LIST_LIKE): 

777 raise ParseError( 

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

779 ) 

780 message.ClearField('values') 

781 for index, item in enumerate(value): 

782 self._ConvertValueMessage( 

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

784 ) 

785 

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

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

788 if not isinstance(value, dict): 

789 raise ParseError( 

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

791 ) 

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

793 # there are no values. 

794 message.Clear() 

795 for key in value: 

796 self._ConvertValueMessage( 

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

798 ) 

799 return 

800 

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

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

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

804 self._ConvertAndSetScalar(message, field, value, path='{0}.value'.format(path)) 

805 

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

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

808 

809 Args: 

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

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

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

813 path: parent path to log parse error info. 

814 

815 Raises: 

816 ParseError: In case of convert problems. 

817 """ 

818 if not isinstance(value, dict): 

819 raise ParseError( 

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

821 field.name, value, path 

822 ) 

823 ) 

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

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

826 for key in value: 

827 key_value = _ConvertScalarFieldValue( 

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

829 ) 

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

831 self.ConvertMessage( 

832 value[key], 

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

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

835 ) 

836 else: 

837 self._ConvertAndSetScalarToMapKey( 

838 message, 

839 field, 

840 key_value, 

841 value[key], 

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

843 

844 def _ConvertAndSetScalarExtension(self, message, extension_field, js_value, path): 

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

846 try: 

847 message.Extensions[extension_field] = _ConvertScalarFieldValue( 

848 js_value, extension_field, path) 

849 except EnumStringValueParseError: 

850 if not self.ignore_unknown_fields: 

851 raise 

852 

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

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

855 try: 

856 setattr( 

857 message, 

858 field.name, 

859 _ConvertScalarFieldValue(js_value, field, path)) 

860 except EnumStringValueParseError: 

861 if not self.ignore_unknown_fields: 

862 raise 

863 

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

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

866 try: 

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

868 _ConvertScalarFieldValue(js_value, repeated_field, path)) 

869 except EnumStringValueParseError: 

870 if not self.ignore_unknown_fields: 

871 raise 

872 

873 def _ConvertAndSetScalarToMapKey(self, message, map_field, converted_key, js_value, path): 

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

875 try: 

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

877 js_value, map_field.message_type.fields_by_name['value'], path, 

878 ) 

879 except EnumStringValueParseError: 

880 if not self.ignore_unknown_fields: 

881 raise 

882 

883 

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

885 """Convert a single scalar field value. 

886 

887 Args: 

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

889 field: The descriptor of the field to convert. 

890 path: parent path to log parse error info. 

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

892 

893 Returns: 

894 The converted scalar field value 

895 

896 Raises: 

897 ParseError: In case of convert problems. 

898 EnumStringValueParseError: In case of unknown enum string value. 

899 """ 

900 try: 

901 if field.cpp_type in _INT_TYPES: 

902 return _ConvertInteger(value) 

903 elif field.cpp_type in _FLOAT_TYPES: 

904 return _ConvertFloat(value, field) 

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

906 return _ConvertBool(value, require_str) 

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

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

909 if isinstance(value, str): 

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

911 else: 

912 encoded = value 

913 # Add extra padding '=' 

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

915 return base64.urlsafe_b64decode(padded_value) 

916 else: 

917 # Checking for unpaired surrogates appears to be unreliable, 

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

919 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

920 raise ParseError('Unpaired surrogate') 

921 return value 

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

923 # Convert an enum value. 

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

925 if enum_value is None: 

926 try: 

927 number = int(value) 

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

929 except ValueError as e: 

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

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

932 raise EnumStringValueParseError( 

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

934 value, field.enum_type.full_name 

935 ) 

936 ) from e 

937 if enum_value is None: 

938 if field.enum_type.is_closed: 

939 raise ParseError( 

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

941 value, field.enum_type.full_name 

942 ) 

943 ) 

944 else: 

945 return number 

946 return enum_value.number 

947 except EnumStringValueParseError as e: 

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

949 except ParseError as e: 

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

951 

952 

953def _ConvertInteger(value): 

954 """Convert an integer. 

955 

956 Args: 

957 value: A scalar value to convert. 

958 

959 Returns: 

960 The integer value. 

961 

962 Raises: 

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

964 """ 

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

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

967 

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

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

970 

971 if isinstance(value, bool): 

972 raise ParseError( 

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

974 ) 

975 

976 try: 

977 return int(value) 

978 except ValueError as e: 

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

980 try: 

981 f = float(value) 

982 except ValueError: 

983 # Raise the original exception for the int parse. 

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

985 if not f.is_integer(): 

986 raise ParseError( 

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

988 ) from e 

989 return int(f) 

990 

991 

992def _ConvertFloat(value, field): 

993 """Convert an floating point number.""" 

994 if isinstance(value, float): 

995 if math.isnan(value): 

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

997 if math.isinf(value): 

998 if value > 0: 

999 raise ParseError( 

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

1001 'use quoted "Infinity" instead' 

1002 ) 

1003 else: 

1004 raise ParseError( 

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

1006 'use quoted "-Infinity" instead' 

1007 ) 

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

1009 # pylint: disable=protected-access 

1010 if value > type_checkers._FLOAT_MAX: 

1011 raise ParseError('Float value too large') 

1012 # pylint: disable=protected-access 

1013 if value < type_checkers._FLOAT_MIN: 

1014 raise ParseError('Float value too small') 

1015 if value == 'nan': 

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

1017 try: 

1018 # Assume Python compatible syntax. 

1019 return float(value) 

1020 except ValueError as e: 

1021 # Check alternative spellings. 

1022 if value == _NEG_INFINITY: 

1023 return float('-inf') 

1024 elif value == _INFINITY: 

1025 return float('inf') 

1026 elif value == _NAN: 

1027 return float('nan') 

1028 else: 

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

1030 

1031 

1032def _ConvertBool(value, require_str): 

1033 """Convert a boolean value. 

1034 

1035 Args: 

1036 value: A scalar value to convert. 

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

1038 

1039 Returns: 

1040 The bool parsed. 

1041 

1042 Raises: 

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

1044 """ 

1045 if require_str: 

1046 if value == 'true': 

1047 return True 

1048 elif value == 'false': 

1049 return False 

1050 else: 

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

1052 

1053 if not isinstance(value, bool): 

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

1055 return value 

1056 

1057 

1058_WKTJSONMETHODS = { 

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

1060 'google.protobuf.Duration': [ 

1061 '_GenericMessageToJsonObject', 

1062 '_ConvertGenericMessage', 

1063 ], 

1064 'google.protobuf.FieldMask': [ 

1065 '_GenericMessageToJsonObject', 

1066 '_ConvertGenericMessage', 

1067 ], 

1068 'google.protobuf.ListValue': [ 

1069 '_ListValueMessageToJsonObject', 

1070 '_ConvertListOrTupleValueMessage', 

1071 ], 

1072 'google.protobuf.Struct': [ 

1073 '_StructMessageToJsonObject', 

1074 '_ConvertStructMessage', 

1075 ], 

1076 'google.protobuf.Timestamp': [ 

1077 '_GenericMessageToJsonObject', 

1078 '_ConvertGenericMessage', 

1079 ], 

1080 'google.protobuf.Value': [ 

1081 '_ValueMessageToJsonObject', 

1082 '_ConvertValueMessage', 

1083 ], 

1084}