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

472 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 

455 try: 

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

457 except Exception as e: 

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

459 

460 try: 

461 return ParseDict( 

462 js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

463 ) 

464 except ParseError as e: 

465 raise e 

466 except Exception as e: 

467 raise ParseError( 

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

469 ) from e 

470 

471 

472def ParseDict( 

473 js_dict, 

474 message, 

475 ignore_unknown_fields=False, 

476 descriptor_pool=None, 

477 max_recursion_depth=100, 

478): 

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

480 

481 Args: 

482 js_dict: Dict representation of a JSON message. 

483 message: A protocol buffer message to merge into. 

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

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

486 default. 

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

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

489 is 100. 

490 

491 Returns: 

492 The same message passed as argument. 

493 """ 

494 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

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

496 return message 

497 

498 

499_INT_OR_FLOAT = (int, float) 

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): 

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 self._ConvertFieldValuePair(value, sub_message, path) 

737 value['@type'] = type_url 

738 # Sets Any message 

739 message.value = sub_message.SerializeToString() 

740 message.type_url = type_url 

741 

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

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

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

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

746 try: 

747 message.FromJsonString(value) 

748 except ValueError as e: 

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

750 

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

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

753 if isinstance(value, dict): 

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

755 elif isinstance(value, list): 

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

757 elif value is None: 

758 message.null_value = 0 

759 elif isinstance(value, bool): 

760 message.bool_value = value 

761 elif isinstance(value, str): 

762 message.string_value = value 

763 elif isinstance(value, _INT_OR_FLOAT): 

764 message.number_value = value 

765 else: 

766 raise ParseError( 

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

768 value, type(value), path 

769 ) 

770 ) 

771 

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

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

774 if not isinstance(value, list): 

775 raise ParseError( 

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

777 ) 

778 message.ClearField('values') 

779 for index, item in enumerate(value): 

780 self._ConvertValueMessage( 

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

782 ) 

783 

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

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

786 if not isinstance(value, dict): 

787 raise ParseError( 

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

789 ) 

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

791 # there are no values. 

792 message.Clear() 

793 for key in value: 

794 self._ConvertValueMessage( 

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

796 ) 

797 return 

798 

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

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

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

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

803 

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

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

806 

807 Args: 

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

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

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

811 path: parent path to log parse error info. 

812 

813 Raises: 

814 ParseError: In case of convert problems. 

815 """ 

816 if not isinstance(value, dict): 

817 raise ParseError( 

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

819 field.name, value, path 

820 ) 

821 ) 

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

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

824 for key in value: 

825 key_value = _ConvertScalarFieldValue( 

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

827 ) 

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

829 self.ConvertMessage( 

830 value[key], 

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

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

833 ) 

834 else: 

835 self._ConvertAndSetScalarToMapKey( 

836 message, 

837 field, 

838 key_value, 

839 value[key], 

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

841 

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

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

844 try: 

845 message.Extensions[extension_field] = _ConvertScalarFieldValue( 

846 js_value, extension_field, path) 

847 except EnumStringValueParseError: 

848 if not self.ignore_unknown_fields: 

849 raise 

850 

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

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

853 try: 

854 setattr( 

855 message, 

856 field.name, 

857 _ConvertScalarFieldValue(js_value, field, path)) 

858 except EnumStringValueParseError: 

859 if not self.ignore_unknown_fields: 

860 raise 

861 

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

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

864 try: 

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

866 _ConvertScalarFieldValue(js_value, repeated_field, path)) 

867 except EnumStringValueParseError: 

868 if not self.ignore_unknown_fields: 

869 raise 

870 

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

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

873 try: 

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

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

876 ) 

877 except EnumStringValueParseError: 

878 if not self.ignore_unknown_fields: 

879 raise 

880 

881 

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

883 """Convert a single scalar field value. 

884 

885 Args: 

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

887 field: The descriptor of the field to convert. 

888 path: parent path to log parse error info. 

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

890 

891 Returns: 

892 The converted scalar field value 

893 

894 Raises: 

895 ParseError: In case of convert problems. 

896 EnumStringValueParseError: In case of unknown enum string value. 

897 """ 

898 try: 

899 if field.cpp_type in _INT_TYPES: 

900 return _ConvertInteger(value) 

901 elif field.cpp_type in _FLOAT_TYPES: 

902 return _ConvertFloat(value, field) 

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

904 return _ConvertBool(value, require_str) 

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

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

907 if isinstance(value, str): 

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

909 else: 

910 encoded = value 

911 # Add extra padding '=' 

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

913 return base64.urlsafe_b64decode(padded_value) 

914 else: 

915 # Checking for unpaired surrogates appears to be unreliable, 

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

917 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

918 raise ParseError('Unpaired surrogate') 

919 return value 

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

921 # Convert an enum value. 

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

923 if enum_value is None: 

924 try: 

925 number = int(value) 

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

927 except ValueError as e: 

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

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

930 raise EnumStringValueParseError( 

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

932 value, field.enum_type.full_name 

933 ) 

934 ) from e 

935 if enum_value is None: 

936 if field.enum_type.is_closed: 

937 raise ParseError( 

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

939 value, field.enum_type.full_name 

940 ) 

941 ) 

942 else: 

943 return number 

944 return enum_value.number 

945 except EnumStringValueParseError as e: 

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

947 except ParseError as e: 

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

949 

950 

951def _ConvertInteger(value): 

952 """Convert an integer. 

953 

954 Args: 

955 value: A scalar value to convert. 

956 

957 Returns: 

958 The integer value. 

959 

960 Raises: 

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

962 """ 

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

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

965 

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

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

968 

969 if isinstance(value, bool): 

970 raise ParseError( 

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

972 ) 

973 

974 return int(value) 

975 

976 

977def _ConvertFloat(value, field): 

978 """Convert an floating point number.""" 

979 if isinstance(value, float): 

980 if math.isnan(value): 

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

982 if math.isinf(value): 

983 if value > 0: 

984 raise ParseError( 

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

986 'use quoted "Infinity" instead' 

987 ) 

988 else: 

989 raise ParseError( 

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

991 'use quoted "-Infinity" instead' 

992 ) 

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

994 # pylint: disable=protected-access 

995 if value > type_checkers._FLOAT_MAX: 

996 raise ParseError('Float value too large') 

997 # pylint: disable=protected-access 

998 if value < type_checkers._FLOAT_MIN: 

999 raise ParseError('Float value too small') 

1000 if value == 'nan': 

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

1002 try: 

1003 # Assume Python compatible syntax. 

1004 return float(value) 

1005 except ValueError as e: 

1006 # Check alternative spellings. 

1007 if value == _NEG_INFINITY: 

1008 return float('-inf') 

1009 elif value == _INFINITY: 

1010 return float('inf') 

1011 elif value == _NAN: 

1012 return float('nan') 

1013 else: 

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

1015 

1016 

1017def _ConvertBool(value, require_str): 

1018 """Convert a boolean value. 

1019 

1020 Args: 

1021 value: A scalar value to convert. 

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

1023 

1024 Returns: 

1025 The bool parsed. 

1026 

1027 Raises: 

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

1029 """ 

1030 if require_str: 

1031 if value == 'true': 

1032 return True 

1033 elif value == 'false': 

1034 return False 

1035 else: 

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

1037 

1038 if not isinstance(value, bool): 

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

1040 return value 

1041 

1042 

1043_WKTJSONMETHODS = { 

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

1045 'google.protobuf.Duration': [ 

1046 '_GenericMessageToJsonObject', 

1047 '_ConvertGenericMessage', 

1048 ], 

1049 'google.protobuf.FieldMask': [ 

1050 '_GenericMessageToJsonObject', 

1051 '_ConvertGenericMessage', 

1052 ], 

1053 'google.protobuf.ListValue': [ 

1054 '_ListValueMessageToJsonObject', 

1055 '_ConvertListValueMessage', 

1056 ], 

1057 'google.protobuf.Struct': [ 

1058 '_StructMessageToJsonObject', 

1059 '_ConvertStructMessage', 

1060 ], 

1061 'google.protobuf.Timestamp': [ 

1062 '_GenericMessageToJsonObject', 

1063 '_ConvertGenericMessage', 

1064 ], 

1065 'google.protobuf.Value': [ 

1066 '_ValueMessageToJsonObject', 

1067 '_ConvertValueMessage', 

1068 ], 

1069}