Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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

467 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 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: 

320 if self.float_format: 

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

322 else: 

323 return type_checkers.ToShortestFloat(value) 

324 

325 return value 

326 

327 def _AnyMessageToJsonObject(self, message): 

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

329 if not message.ListFields(): 

330 return {} 

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

332 js = OrderedDict() 

333 type_url = message.type_url 

334 js['@type'] = type_url 

335 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

336 sub_message.ParseFromString(message.value) 

337 message_descriptor = sub_message.DESCRIPTOR 

338 full_name = message_descriptor.full_name 

339 if _IsWrapperMessage(message_descriptor): 

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

341 return js 

342 if full_name in _WKTJSONMETHODS: 

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

344 self 

345 ) 

346 return js 

347 return self._RegularMessageToJsonObject(sub_message, js) 

348 

349 def _GenericMessageToJsonObject(self, message): 

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

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

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

353 return message.ToJsonString() 

354 

355 def _ValueMessageToJsonObject(self, message): 

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

357 which = message.WhichOneof('kind') 

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

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

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

361 return None 

362 if which == 'list_value': 

363 return self._ListValueMessageToJsonObject(message.list_value) 

364 if which == 'number_value': 

365 value = message.number_value 

366 if math.isinf(value): 

367 raise ValueError( 

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

369 'which would parse as string_value' 

370 ) 

371 if math.isnan(value): 

372 raise ValueError( 

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

374 'which would parse as string_value' 

375 ) 

376 else: 

377 value = getattr(message, which) 

378 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 

379 return self._FieldToJsonObject(oneof_descriptor, value) 

380 

381 def _ListValueMessageToJsonObject(self, message): 

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

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

384 

385 def _StructMessageToJsonObject(self, message): 

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

387 fields = message.fields 

388 ret = {} 

389 for key in fields: 

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

391 return ret 

392 

393 def _WrapperMessageToJsonObject(self, message): 

394 return self._FieldToJsonObject( 

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

396 ) 

397 

398 

399def _IsWrapperMessage(message_descriptor): 

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

401 

402 

403def _DuplicateChecker(js): 

404 result = {} 

405 for name, value in js: 

406 if name in result: 

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

408 result[name] = value 

409 return result 

410 

411 

412def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 

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

414 db = symbol_database.Default() 

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

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

417 try: 

418 message_descriptor = pool.FindMessageTypeByName(type_name) 

419 except KeyError as e: 

420 raise TypeError( 

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

422 ) from e 

423 message_class = message_factory.GetMessageClass(message_descriptor) 

424 return message_class() 

425 

426 

427def Parse( 

428 text, 

429 message, 

430 ignore_unknown_fields=False, 

431 descriptor_pool=None, 

432 max_recursion_depth=100, 

433): 

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

435 

436 Args: 

437 text: Message JSON representation. 

438 message: A protocol buffer message to merge into. 

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

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

441 default. 

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

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

444 is 100. 

445 

446 Returns: 

447 The same message passed as argument. 

448 

449 Raises:: 

450 ParseError: On JSON parsing problems. 

451 """ 

452 if not isinstance(text, str): 

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

454 try: 

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

456 except ValueError as e: 

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

458 return ParseDict( 

459 js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

460 ) 

461 

462 

463def ParseDict( 

464 js_dict, 

465 message, 

466 ignore_unknown_fields=False, 

467 descriptor_pool=None, 

468 max_recursion_depth=100, 

469): 

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

471 

472 Args: 

473 js_dict: Dict representation of a JSON message. 

474 message: A protocol buffer message to merge into. 

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

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

477 default. 

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

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

480 is 100. 

481 

482 Returns: 

483 The same message passed as argument. 

484 """ 

485 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

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

487 return message 

488 

489 

490_INT_OR_FLOAT = (int, float) 

491 

492 

493class _Parser(object): 

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

495 

496 def __init__( 

497 self, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

498 ): 

499 self.ignore_unknown_fields = ignore_unknown_fields 

500 self.descriptor_pool = descriptor_pool 

501 self.max_recursion_depth = max_recursion_depth 

502 self.recursion_depth = 0 

503 

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

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

506 

507 Args: 

508 value: A JSON object. 

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

510 path: parent path to log parse error info. 

511 

512 Raises: 

513 ParseError: In case of convert problems. 

514 """ 

515 self.recursion_depth += 1 

516 if self.recursion_depth > self.max_recursion_depth: 

517 raise ParseError( 

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

519 self.max_recursion_depth 

520 ) 

521 ) 

522 message_descriptor = message.DESCRIPTOR 

523 full_name = message_descriptor.full_name 

524 if not path: 

525 path = message_descriptor.name 

526 if _IsWrapperMessage(message_descriptor): 

527 self._ConvertWrapperMessage(value, message, path) 

528 elif full_name in _WKTJSONMETHODS: 

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

530 else: 

531 self._ConvertFieldValuePair(value, message, path) 

532 self.recursion_depth -= 1 

533 

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

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

536 

537 Args: 

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

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

540 path: parent path to log parse error info. 

541 

542 Raises: 

543 ParseError: In case of problems converting. 

544 """ 

545 names = [] 

546 message_descriptor = message.DESCRIPTOR 

547 fields_by_json_name = dict( 

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

549 ) 

550 for name in js: 

551 try: 

552 field = fields_by_json_name.get(name, None) 

553 if not field: 

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

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

556 if not message_descriptor.is_extendable: 

557 raise ParseError( 

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

559 message_descriptor.full_name, path 

560 ) 

561 ) 

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

563 # pylint: disable=protected-access 

564 field = message.Extensions._FindExtensionByName(identifier) 

565 # pylint: enable=protected-access 

566 if not field: 

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

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

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

570 # pylint: disable=protected-access 

571 field = message.Extensions._FindExtensionByName(identifier) 

572 # pylint: enable=protected-access 

573 if not field: 

574 if self.ignore_unknown_fields: 

575 continue 

576 raise ParseError( 

577 ( 

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

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

580 ).format( 

581 message_descriptor.full_name, 

582 name, 

583 path, 

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

585 ) 

586 ) 

587 if name in names: 

588 raise ParseError( 

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

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

591 message.DESCRIPTOR.full_name, name, path 

592 ) 

593 ) 

594 names.append(name) 

595 value = js[name] 

596 # Check no other oneof field is parsed. 

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

598 oneof_name = field.containing_oneof.name 

599 if oneof_name in names: 

600 raise ParseError( 

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

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

603 message.DESCRIPTOR.full_name, oneof_name, path 

604 ) 

605 ) 

606 names.append(oneof_name) 

607 

608 if value is None: 

609 if ( 

610 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

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

612 ): 

613 sub_message = getattr(message, field.name) 

614 sub_message.null_value = 0 

615 elif ( 

616 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

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

618 ): 

619 setattr(message, field.name, 0) 

620 else: 

621 message.ClearField(field.name) 

622 continue 

623 

624 # Parse field value. 

625 if _IsMapEntry(field): 

626 message.ClearField(field.name) 

627 self._ConvertMapFieldValue( 

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

629 ) 

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

631 message.ClearField(field.name) 

632 if not isinstance(value, list): 

633 raise ParseError( 

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

635 name, value, path 

636 ) 

637 ) 

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

639 # Repeated message field. 

640 for index, item in enumerate(value): 

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

642 # None is a null_value in Value. 

643 if ( 

644 item is None 

645 and sub_message.DESCRIPTOR.full_name 

646 != 'google.protobuf.Value' 

647 ): 

648 raise ParseError( 

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

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

651 path, name, index 

652 ) 

653 ) 

654 self.ConvertMessage( 

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

656 ) 

657 else: 

658 # Repeated scalar field. 

659 for index, item in enumerate(value): 

660 if item is None: 

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._ConvertAndAppendScalar( 

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

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

670 if field.is_extension: 

671 sub_message = message.Extensions[field] 

672 else: 

673 sub_message = getattr(message, field.name) 

674 sub_message.SetInParent() 

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

676 else: 

677 if field.is_extension: 

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

679 else: 

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

681 except ParseError as e: 

682 if field and field.containing_oneof is None: 

683 raise ParseError( 

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

685 ) from e 

686 else: 

687 raise ParseError(str(e)) from e 

688 except ValueError as e: 

689 raise ParseError( 

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

691 ) from e 

692 except TypeError as e: 

693 raise ParseError( 

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

695 ) from e 

696 

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

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

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

700 return 

701 try: 

702 type_url = value['@type'] 

703 except KeyError as e: 

704 raise ParseError( 

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

706 ) from e 

707 

708 try: 

709 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

710 except TypeError as e: 

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

712 message_descriptor = sub_message.DESCRIPTOR 

713 full_name = message_descriptor.full_name 

714 if _IsWrapperMessage(message_descriptor): 

715 self._ConvertWrapperMessage( 

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

717 ) 

718 elif full_name in _WKTJSONMETHODS: 

719 methodcaller( 

720 _WKTJSONMETHODS[full_name][1], 

721 value['value'], 

722 sub_message, 

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

724 )(self) 

725 else: 

726 del value['@type'] 

727 self._ConvertFieldValuePair(value, sub_message, path) 

728 value['@type'] = type_url 

729 # Sets Any message 

730 message.value = sub_message.SerializeToString() 

731 message.type_url = type_url 

732 

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

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

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

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

737 try: 

738 message.FromJsonString(value) 

739 except ValueError as e: 

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

741 

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

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

744 if isinstance(value, dict): 

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

746 elif isinstance(value, list): 

747 self._ConvertListValueMessage(value, message.list_value, path) 

748 elif value is None: 

749 message.null_value = 0 

750 elif isinstance(value, bool): 

751 message.bool_value = value 

752 elif isinstance(value, str): 

753 message.string_value = value 

754 elif isinstance(value, _INT_OR_FLOAT): 

755 message.number_value = value 

756 else: 

757 raise ParseError( 

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

759 value, type(value), path 

760 ) 

761 ) 

762 

763 def _ConvertListValueMessage(self, value, message, path): 

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

765 if not isinstance(value, list): 

766 raise ParseError( 

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

768 ) 

769 message.ClearField('values') 

770 for index, item in enumerate(value): 

771 self._ConvertValueMessage( 

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

773 ) 

774 

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

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

777 if not isinstance(value, dict): 

778 raise ParseError( 

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

780 ) 

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

782 # there are no values. 

783 message.Clear() 

784 for key in value: 

785 self._ConvertValueMessage( 

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

787 ) 

788 return 

789 

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

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

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

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

794 

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

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

797 

798 Args: 

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

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

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

802 path: parent path to log parse error info. 

803 

804 Raises: 

805 ParseError: In case of convert problems. 

806 """ 

807 if not isinstance(value, dict): 

808 raise ParseError( 

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

810 field.name, value, path 

811 ) 

812 ) 

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

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

815 for key in value: 

816 key_value = _ConvertScalarFieldValue( 

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

818 ) 

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

820 self.ConvertMessage( 

821 value[key], 

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

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

824 ) 

825 else: 

826 self._ConvertAndSetScalarToMapKey( 

827 message, 

828 field, 

829 key_value, 

830 value[key], 

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

832 

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

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

835 try: 

836 message.Extensions[extension_field] = _ConvertScalarFieldValue( 

837 js_value, extension_field, path) 

838 except EnumStringValueParseError: 

839 if not self.ignore_unknown_fields: 

840 raise 

841 

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

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

844 try: 

845 setattr( 

846 message, 

847 field.name, 

848 _ConvertScalarFieldValue(js_value, field, path)) 

849 except EnumStringValueParseError: 

850 if not self.ignore_unknown_fields: 

851 raise 

852 

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

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

855 try: 

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

857 _ConvertScalarFieldValue(js_value, repeated_field, path)) 

858 except EnumStringValueParseError: 

859 if not self.ignore_unknown_fields: 

860 raise 

861 

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

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

864 try: 

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

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

867 ) 

868 except EnumStringValueParseError: 

869 if not self.ignore_unknown_fields: 

870 raise 

871 

872 

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

874 """Convert a single scalar field value. 

875 

876 Args: 

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

878 field: The descriptor of the field to convert. 

879 path: parent path to log parse error info. 

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

881 

882 Returns: 

883 The converted scalar field value 

884 

885 Raises: 

886 ParseError: In case of convert problems. 

887 EnumStringValueParseError: In case of unknown enum string value. 

888 """ 

889 try: 

890 if field.cpp_type in _INT_TYPES: 

891 return _ConvertInteger(value) 

892 elif field.cpp_type in _FLOAT_TYPES: 

893 return _ConvertFloat(value, field) 

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

895 return _ConvertBool(value, require_str) 

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

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

898 if isinstance(value, str): 

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

900 else: 

901 encoded = value 

902 # Add extra padding '=' 

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

904 return base64.urlsafe_b64decode(padded_value) 

905 else: 

906 # Checking for unpaired surrogates appears to be unreliable, 

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

908 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

909 raise ParseError('Unpaired surrogate') 

910 return value 

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

912 # Convert an enum value. 

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

914 if enum_value is None: 

915 try: 

916 number = int(value) 

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

918 except ValueError as e: 

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

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

921 raise EnumStringValueParseError( 

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

923 value, field.enum_type.full_name 

924 ) 

925 ) from e 

926 if enum_value is None: 

927 if field.enum_type.is_closed: 

928 raise ParseError( 

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

930 value, field.enum_type.full_name 

931 ) 

932 ) 

933 else: 

934 return number 

935 return enum_value.number 

936 except EnumStringValueParseError as e: 

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

938 except ParseError as e: 

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

940 

941 

942def _ConvertInteger(value): 

943 """Convert an integer. 

944 

945 Args: 

946 value: A scalar value to convert. 

947 

948 Returns: 

949 The integer value. 

950 

951 Raises: 

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

953 """ 

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

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

956 

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

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

959 

960 if isinstance(value, bool): 

961 raise ParseError( 

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

963 ) 

964 

965 return int(value) 

966 

967 

968def _ConvertFloat(value, field): 

969 """Convert an floating point number.""" 

970 if isinstance(value, float): 

971 if math.isnan(value): 

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

973 if math.isinf(value): 

974 if value > 0: 

975 raise ParseError( 

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

977 'use quoted "Infinity" instead' 

978 ) 

979 else: 

980 raise ParseError( 

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

982 'use quoted "-Infinity" instead' 

983 ) 

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

985 # pylint: disable=protected-access 

986 if value > type_checkers._FLOAT_MAX: 

987 raise ParseError('Float value too large') 

988 # pylint: disable=protected-access 

989 if value < type_checkers._FLOAT_MIN: 

990 raise ParseError('Float value too small') 

991 if value == 'nan': 

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

993 try: 

994 # Assume Python compatible syntax. 

995 return float(value) 

996 except ValueError as e: 

997 # Check alternative spellings. 

998 if value == _NEG_INFINITY: 

999 return float('-inf') 

1000 elif value == _INFINITY: 

1001 return float('inf') 

1002 elif value == _NAN: 

1003 return float('nan') 

1004 else: 

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

1006 

1007 

1008def _ConvertBool(value, require_str): 

1009 """Convert a boolean value. 

1010 

1011 Args: 

1012 value: A scalar value to convert. 

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

1014 

1015 Returns: 

1016 The bool parsed. 

1017 

1018 Raises: 

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

1020 """ 

1021 if require_str: 

1022 if value == 'true': 

1023 return True 

1024 elif value == 'false': 

1025 return False 

1026 else: 

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

1028 

1029 if not isinstance(value, bool): 

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

1031 return value 

1032 

1033 

1034_WKTJSONMETHODS = { 

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

1036 'google.protobuf.Duration': [ 

1037 '_GenericMessageToJsonObject', 

1038 '_ConvertGenericMessage', 

1039 ], 

1040 'google.protobuf.FieldMask': [ 

1041 '_GenericMessageToJsonObject', 

1042 '_ConvertGenericMessage', 

1043 ], 

1044 'google.protobuf.ListValue': [ 

1045 '_ListValueMessageToJsonObject', 

1046 '_ConvertListValueMessage', 

1047 ], 

1048 'google.protobuf.Struct': [ 

1049 '_StructMessageToJsonObject', 

1050 '_ConvertStructMessage', 

1051 ], 

1052 'google.protobuf.Timestamp': [ 

1053 '_GenericMessageToJsonObject', 

1054 '_ConvertGenericMessage', 

1055 ], 

1056 'google.protobuf.Value': [ 

1057 '_ValueMessageToJsonObject', 

1058 '_ConvertValueMessage', 

1059 ], 

1060}