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

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

490 statements  

1# Protocol Buffers - Google's data interchange format 

2# Copyright 2008 Google Inc. All rights reserved. 

3# 

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

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

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

7 

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

9 

10Simple usage example: 

11 

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

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

14 json_string = json_format.MessageToJson(message) 

15 

16 # Parse a json format string to proto object. 

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

18""" 

19 

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

21 

22 

23import base64 

24from collections import OrderedDict 

25import json 

26import math 

27from operator import methodcaller 

28import re 

29 

30from google.protobuf import descriptor 

31from google.protobuf import message_factory 

32from google.protobuf import symbol_database 

33from google.protobuf.internal import type_checkers 

34 

35 

36_INT_TYPES = frozenset([ 

37 descriptor.FieldDescriptor.CPPTYPE_INT32, 

38 descriptor.FieldDescriptor.CPPTYPE_UINT32, 

39 descriptor.FieldDescriptor.CPPTYPE_INT64, 

40 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

41]) 

42_INT64_TYPES = frozenset([ 

43 descriptor.FieldDescriptor.CPPTYPE_INT64, 

44 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

45]) 

46_FLOAT_TYPES = frozenset([ 

47 descriptor.FieldDescriptor.CPPTYPE_FLOAT, 

48 descriptor.FieldDescriptor.CPPTYPE_DOUBLE, 

49]) 

50_INFINITY = 'Infinity' 

51_NEG_INFINITY = '-Infinity' 

52_NAN = 'NaN' 

53 

54_UNPAIRED_SURROGATE_PATTERN = re.compile( 

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

56) 

57 

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

59 

60 

61class Error(Exception): 

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

63 

64 

65class SerializeToJsonError(Error): 

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

67 

68 

69class ParseError(Error): 

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

71 

72 

73class EnumStringValueParseError(ParseError): 

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

75 

76 This exception is suppressed if ignore_unknown_fields is set. 

77 """ 

78 

79 

80def MessageToJson( 

81 message, 

82 preserving_proto_field_name=False, 

83 indent=2, 

84 sort_keys=False, 

85 use_integers_for_enums=False, 

86 descriptor_pool=None, 

87 ensure_ascii=True, 

88 always_print_fields_with_no_presence=False, 

89 *, 

90 unquote_int64_if_possible=False, 

91): 

92 """Converts protobuf message to JSON format. 

93 

94 Args: 

95 message: The protocol buffers message instance to serialize. 

96 always_print_fields_with_no_presence: If True, fields without presence 

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

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

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

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

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

102 lowerCamelCase. 

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

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

105 level is None, no newlines will be inserted. 

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

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

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

109 default. 

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

111 False, Unicode strings are returned unchanged. 

112 unquote_int64_if_possible: If True, unquote int64 fields for values that 

113 are safe to emit as numbers (all values smaller than 2^53 and a sparse 

114 set of values that are larger). 

115 

116 Returns: 

117 A string containing the JSON formatted protocol buffer message. 

118 """ 

119 printer = _Printer( 

120 preserving_proto_field_name, 

121 use_integers_for_enums, 

122 descriptor_pool, 

123 always_print_fields_with_no_presence, 

124 unquote_int64_if_possible=unquote_int64_if_possible, 

125 ) 

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

127 

128 

129def MessageToDict( 

130 message, 

131 always_print_fields_with_no_presence=False, 

132 preserving_proto_field_name=False, 

133 use_integers_for_enums=False, 

134 descriptor_pool=None, 

135 *, 

136 unquote_int64_if_possible=False, 

137): 

138 """Converts protobuf message to a dictionary. 

139 

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

141 

142 Args: 

143 message: The protocol buffers message instance to serialize. 

144 always_print_fields_with_no_presence: If True, fields without presence 

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

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

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

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

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

150 lowerCamelCase. 

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

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

153 default. 

154 unquote_int64_if_possible: If True, unquote int64 fields for values that 

155 are safe to emit as numbers (all values smaller than 2^53 and a sparse 

156 set of values that are larger). 

157 

158 Returns: 

159 A dict representation of the protocol buffer message. 

160 """ 

161 printer = _Printer( 

162 preserving_proto_field_name, 

163 use_integers_for_enums, 

164 descriptor_pool, 

165 always_print_fields_with_no_presence, 

166 unquote_int64_if_possible=unquote_int64_if_possible, 

167 ) 

168 # pylint: disable=protected-access 

169 return printer._MessageToJsonObject(message) 

170 

171 

172def _IsMapEntry(field): 

173 return ( 

174 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE 

175 and field.message_type.has_options 

176 and field.message_type.GetOptions().map_entry 

177 ) 

178 

179 

180class _Printer(object): 

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

182 

183 def __init__( 

184 self, 

185 preserving_proto_field_name=False, 

186 use_integers_for_enums=False, 

187 descriptor_pool=None, 

188 always_print_fields_with_no_presence=False, 

189 *, 

190 unquote_int64_if_possible=False, 

191 ): 

192 self.always_print_fields_with_no_presence = ( 

193 always_print_fields_with_no_presence 

194 ) 

195 self.preserving_proto_field_name = preserving_proto_field_name 

196 self.use_integers_for_enums = use_integers_for_enums 

197 self.descriptor_pool = descriptor_pool 

198 self.unquote_int64_if_possible = unquote_int64_if_possible 

199 

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

201 js = self._MessageToJsonObject(message) 

202 return json.dumps( 

203 js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii 

204 ) 

205 

206 def _MessageToJsonObject(self, message): 

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

208 message_descriptor = message.DESCRIPTOR 

209 full_name = message_descriptor.full_name 

210 if _IsWrapperMessage(message_descriptor): 

211 return self._WrapperMessageToJsonObject(message) 

212 if full_name in _WKTJSONMETHODS: 

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

214 js = {} 

215 return self._RegularMessageToJsonObject(message, js) 

216 

217 def _RegularMessageToJsonObject(self, message, js): 

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

219 fields = message.ListFields() 

220 

221 try: 

222 for field, value in fields: 

223 if field.is_extension: 

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

225 elif self.preserving_proto_field_name: 

226 name = field.name 

227 else: 

228 name = field.json_name 

229 

230 if _IsMapEntry(field): 

231 # Convert a map field. 

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

233 js_map = {} 

234 for key in value: 

235 if isinstance(key, bool): 

236 if key: 

237 recorded_key = 'true' 

238 else: 

239 recorded_key = 'false' 

240 else: 

241 recorded_key = str(key) 

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

243 js[name] = js_map 

244 elif field.is_repeated: 

245 # Convert a repeated field. 

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

247 else: 

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

249 

250 # Serialize default value if including_default_value_fields is True. 

251 if ( 

252 self.always_print_fields_with_no_presence 

253 ): 

254 message_descriptor = message.DESCRIPTOR 

255 for field in message_descriptor.fields: 

256 

257 # always_print_fields_with_no_presence doesn't apply to 

258 # any field which supports presence. 

259 if self.always_print_fields_with_no_presence and field.has_presence: 

260 continue 

261 

262 if self.preserving_proto_field_name: 

263 name = field.name 

264 else: 

265 name = field.json_name 

266 if name in js: 

267 # Skip the field which has been serialized already. 

268 continue 

269 if _IsMapEntry(field): 

270 js[name] = {} 

271 elif field.is_repeated: 

272 js[name] = [] 

273 else: 

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

275 

276 except ValueError as e: 

277 raise SerializeToJsonError( 

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

279 ) from e 

280 

281 return js 

282 

283 def _FieldToJsonObject(self, field, value): 

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

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

286 return self._MessageToJsonObject(value) 

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

288 if self.use_integers_for_enums: 

289 return value 

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

291 return None 

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

293 if enum_value is not None: 

294 return enum_value.name 

295 else: 

296 if field.enum_type.is_closed: 

297 raise SerializeToJsonError( 

298 'Enum field contains an integer value ' 

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

300 ) 

301 else: 

302 return value 

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

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

305 # Use base64 Data encoding for bytes 

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

307 else: 

308 return str(value) 

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

310 return bool(value) 

311 elif field.cpp_type in _INT64_TYPES: 

312 if self.unquote_int64_if_possible and float(value) == value: 

313 return value 

314 else: 

315 return str(value) 

316 elif field.cpp_type in _FLOAT_TYPES: 

317 if math.isinf(value): 

318 if value < 0.0: 

319 return _NEG_INFINITY 

320 else: 

321 return _INFINITY 

322 if math.isnan(value): 

323 return _NAN 

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

325 return type_checkers.ToShortestFloat(value) 

326 

327 return value 

328 

329 def _AnyMessageToJsonObject(self, message): 

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

331 if not message.ListFields(): 

332 return {} 

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

334 js = OrderedDict() 

335 type_url = message.type_url 

336 js['@type'] = type_url 

337 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

338 sub_message.ParseFromString(message.value) 

339 message_descriptor = sub_message.DESCRIPTOR 

340 full_name = message_descriptor.full_name 

341 if _IsWrapperMessage(message_descriptor): 

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

343 return js 

344 if full_name in _WKTJSONMETHODS: 

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

346 self 

347 ) 

348 return js 

349 return self._RegularMessageToJsonObject(sub_message, js) 

350 

351 def _GenericMessageToJsonObject(self, message): 

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

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

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

355 return message.ToJsonString() 

356 

357 def _ValueMessageToJsonObject(self, message): 

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

359 which = message.WhichOneof('kind') 

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

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

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

363 return None 

364 if which == 'list_value': 

365 return self._ListValueMessageToJsonObject(message.list_value) 

366 if which == 'number_value': 

367 value = message.number_value 

368 if math.isinf(value): 

369 raise ValueError( 

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

371 'which would parse as string_value' 

372 ) 

373 if math.isnan(value): 

374 raise ValueError( 

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

376 'which would parse as string_value' 

377 ) 

378 else: 

379 value = getattr(message, which) 

380 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 

381 return self._FieldToJsonObject(oneof_descriptor, value) 

382 

383 def _ListValueMessageToJsonObject(self, message): 

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

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

386 

387 def _StructMessageToJsonObject(self, message): 

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

389 fields = message.fields 

390 ret = {} 

391 for key in fields: 

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

393 return ret 

394 

395 def _WrapperMessageToJsonObject(self, message): 

396 return self._FieldToJsonObject( 

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

398 ) 

399 

400 

401def _IsWrapperMessage(message_descriptor): 

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

403 

404 

405def _DuplicateChecker(js): 

406 result = {} 

407 for name, value in js: 

408 if name in result: 

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

410 result[name] = value 

411 return result 

412 

413 

414def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 

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

416 db = symbol_database.Default() 

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

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

419 try: 

420 message_descriptor = pool.FindMessageTypeByName(type_name) 

421 except KeyError as e: 

422 raise TypeError( 

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

424 ) from e 

425 message_class = message_factory.GetMessageClass(message_descriptor) 

426 return message_class() 

427 

428 

429def Parse( 

430 text, 

431 message, 

432 ignore_unknown_fields=False, 

433 descriptor_pool=None, 

434 max_recursion_depth=100, 

435): 

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

437 

438 Args: 

439 text: Message JSON representation. 

440 message: A protocol buffer message to merge into. 

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

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

443 default. 

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

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

446 is 100. 

447 

448 Returns: 

449 The same message passed as argument. 

450 

451 Raises:: 

452 ParseError: On JSON parsing problems. 

453 """ 

454 if not isinstance(text, str): 

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

456 

457 try: 

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

459 except Exception as e: 

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

461 

462 try: 

463 return ParseDict( 

464 js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

465 ) 

466 except ParseError as e: 

467 raise e 

468 except Exception as e: 

469 raise ParseError( 

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

471 ) from e 

472 

473 

474def ParseDict( 

475 js_dict, 

476 message, 

477 ignore_unknown_fields=False, 

478 descriptor_pool=None, 

479 max_recursion_depth=100, 

480): 

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

482 

483 Args: 

484 js_dict: Dict representation of a JSON message. 

485 message: A protocol buffer message to merge into. 

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

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

488 default. 

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

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

491 is 100. 

492 

493 Returns: 

494 The same message passed as argument. 

495 """ 

496 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

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

498 return message 

499 

500 

501_INT_OR_FLOAT = (int, float) 

502_LIST_LIKE = (list, tuple) 

503 

504 

505class _Parser(object): 

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

507 

508 def __init__( 

509 self, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

510 ): 

511 self.ignore_unknown_fields = ignore_unknown_fields 

512 self.descriptor_pool = descriptor_pool 

513 self.max_recursion_depth = max_recursion_depth 

514 self.recursion_depth = 0 

515 

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

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

518 

519 Args: 

520 value: A JSON object. 

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

522 path: parent path to log parse error info. 

523 

524 Raises: 

525 ParseError: In case of convert problems. 

526 """ 

527 # Increment recursion depth at message entry. The max_recursion_depth limit 

528 # is exclusive: a depth value equal to max_recursion_depth will trigger an 

529 # error. For example, with max_recursion_depth=5, nesting up to depth 4 is 

530 # allowed, but attempting depth 5 raises ParseError. 

531 self.recursion_depth += 1 

532 if self.recursion_depth > self.max_recursion_depth: 

533 raise ParseError( 

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

535 self.max_recursion_depth 

536 ) 

537 ) 

538 message_descriptor = message.DESCRIPTOR 

539 full_name = message_descriptor.full_name 

540 if not path: 

541 path = message_descriptor.name 

542 if _IsWrapperMessage(message_descriptor): 

543 self._ConvertWrapperMessage(value, message, path) 

544 elif full_name in _WKTJSONMETHODS: 

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

546 else: 

547 self._ConvertFieldValuePair(value, message, path) 

548 self.recursion_depth -= 1 

549 

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

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

552 

553 Args: 

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

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

556 path: parent path to log parse error info. 

557 

558 Raises: 

559 ParseError: In case of problems converting. 

560 """ 

561 names = [] 

562 message_descriptor = message.DESCRIPTOR 

563 fields_by_json_name = dict( 

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

565 ) 

566 

567 def _ClearFieldOrExtension(message, field): 

568 if field.is_extension: 

569 message.ClearExtension(field) 

570 else: 

571 message.ClearField(field.name) 

572 

573 def _GetFieldOrExtension(message, field): 

574 if field.is_extension: 

575 return message.Extensions[field] 

576 else: 

577 return getattr(message, field.name) 

578 

579 def _SetFieldOrExtension(message, field, value): 

580 if field.is_extension: 

581 message.Extensions[field] = value 

582 else: 

583 setattr(message, field.name, value) 

584 

585 for name in js: 

586 try: 

587 field = fields_by_json_name.get(name, None) 

588 if not field: 

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

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

591 if not message_descriptor.is_extendable: 

592 raise ParseError( 

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

594 message_descriptor.full_name, path 

595 ) 

596 ) 

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

598 # pylint: disable=protected-access 

599 field = message.Extensions._FindExtensionByName(identifier) 

600 # pylint: enable=protected-access 

601 if not field: 

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

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

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

605 # pylint: disable=protected-access 

606 field = message.Extensions._FindExtensionByName(identifier) 

607 # pylint: enable=protected-access 

608 if not field: 

609 if self.ignore_unknown_fields: 

610 continue 

611 raise ParseError( 

612 ( 

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

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

615 ).format( 

616 message_descriptor.full_name, 

617 name, 

618 path, 

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

620 ) 

621 ) 

622 if name in names: 

623 raise ParseError( 

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

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

626 message.DESCRIPTOR.full_name, name, path 

627 ) 

628 ) 

629 names.append(name) 

630 value = js[name] 

631 # Check no other oneof field is parsed. 

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

633 oneof_name = field.containing_oneof.name 

634 if oneof_name in names: 

635 raise ParseError( 

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

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

638 message.DESCRIPTOR.full_name, oneof_name, path 

639 ) 

640 ) 

641 names.append(oneof_name) 

642 

643 if value is None: 

644 if ( 

645 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

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

647 ): 

648 sub_message = _GetFieldOrExtension(message, field) 

649 sub_message.null_value = 0 

650 elif ( 

651 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

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

653 ): 

654 _SetFieldOrExtension(message, field, 0) 

655 else: 

656 _ClearFieldOrExtension(message, field) 

657 continue 

658 

659 # Parse field value. 

660 if _IsMapEntry(field): 

661 _ClearFieldOrExtension(message, field) 

662 self._ConvertMapFieldValue( 

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

664 ) 

665 elif field.is_repeated: 

666 _ClearFieldOrExtension(message, field) 

667 if not isinstance(value, _LIST_LIKE): 

668 raise ParseError( 

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

670 name, value, path 

671 ) 

672 ) 

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

674 # Repeated message field. 

675 for index, item in enumerate(value): 

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

677 # None is a null_value in Value. 

678 if ( 

679 item is None 

680 and sub_message.DESCRIPTOR.full_name 

681 != 'google.protobuf.Value' 

682 ): 

683 raise ParseError( 

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

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

686 path, name, index 

687 ) 

688 ) 

689 self.ConvertMessage( 

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

691 ) 

692 else: 

693 # Repeated scalar field. 

694 for index, item in enumerate(value): 

695 if item is None: 

696 raise ParseError( 

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

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

699 path, name, index 

700 ) 

701 ) 

702 self._ConvertAndAppendScalar( 

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

704 ) 

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

706 sub_message = _GetFieldOrExtension(message, field) 

707 sub_message.SetInParent() 

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

709 else: 

710 self._ConvertAndSetScalar( 

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

712 ) 

713 except ParseError as e: 

714 if field and field.containing_oneof is None: 

715 raise ParseError( 

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

717 ) from e 

718 else: 

719 raise ParseError(str(e)) from e 

720 except ValueError as e: 

721 raise ParseError( 

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

723 ) from e 

724 except TypeError as e: 

725 raise ParseError( 

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

727 ) from e 

728 

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

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

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

732 return 

733 try: 

734 type_url = value['@type'] 

735 except KeyError as e: 

736 raise ParseError( 

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

738 ) from e 

739 

740 try: 

741 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

742 except TypeError as e: 

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

744 message_descriptor = sub_message.DESCRIPTOR 

745 full_name = message_descriptor.full_name 

746 if _IsWrapperMessage(message_descriptor): 

747 self._ConvertWrapperMessage( 

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

749 ) 

750 elif full_name in _WKTJSONMETHODS: 

751 # For well-known types (including nested Any), use ConvertMessage 

752 # to ensure recursion depth is properly tracked 

753 self.ConvertMessage( 

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

755 ) 

756 else: 

757 del value['@type'] 

758 try: 

759 self._ConvertFieldValuePair(value, sub_message, path) 

760 finally: 

761 value['@type'] = type_url 

762 # Sets Any message 

763 message.value = sub_message.SerializeToString() 

764 message.type_url = type_url 

765 

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

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

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

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

770 try: 

771 message.FromJsonString(value) 

772 except ValueError as e: 

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

774 

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

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

777 if isinstance(value, dict): 

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

779 elif isinstance(value, _LIST_LIKE): 

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

781 elif value is None: 

782 message.null_value = 0 

783 elif isinstance(value, bool): 

784 message.bool_value = value 

785 elif isinstance(value, str): 

786 message.string_value = value 

787 elif isinstance(value, _INT_OR_FLOAT): 

788 message.number_value = value 

789 else: 

790 raise ParseError( 

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

792 value, type(value), path 

793 ) 

794 ) 

795 

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

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

798 if not isinstance(value, _LIST_LIKE): 

799 raise ParseError( 

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

801 ) 

802 message.ClearField('values') 

803 for index, item in enumerate(value): 

804 self._ConvertValueMessage( 

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

806 ) 

807 

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

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

810 if not isinstance(value, dict): 

811 raise ParseError( 

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

813 ) 

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

815 # there are no values. 

816 message.Clear() 

817 for key in value: 

818 self._ConvertValueMessage( 

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

820 ) 

821 return 

822 

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

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

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

826 self._ConvertAndSetScalar( 

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

828 ) 

829 

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

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

832 

833 Args: 

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

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

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

837 path: parent path to log parse error info. 

838 

839 Raises: 

840 ParseError: In case of convert problems. 

841 """ 

842 if not isinstance(value, dict): 

843 raise ParseError( 

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

845 field.name, value, path 

846 ) 

847 ) 

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

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

850 for key in value: 

851 key_value = _ConvertScalarFieldValue( 

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

853 ) 

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

855 self.ConvertMessage( 

856 value[key], 

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

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

859 ) 

860 else: 

861 self._ConvertAndSetScalarToMapKey( 

862 message, 

863 field, 

864 key_value, 

865 value[key], 

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

867 ) 

868 

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

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

871 try: 

872 value = _ConvertScalarFieldValue(js_value, field, path) 

873 if field.is_extension: 

874 message.Extensions[field] = value 

875 else: 

876 setattr(message, field.name, value) 

877 except EnumStringValueParseError: 

878 if not self.ignore_unknown_fields: 

879 raise 

880 

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

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

883 try: 

884 if repeated_field.is_extension: 

885 repeated = message.Extensions[repeated_field] 

886 else: 

887 repeated = getattr(message, repeated_field.name) 

888 value = _ConvertScalarFieldValue(js_value, repeated_field, path) 

889 repeated.append(value) 

890 except EnumStringValueParseError: 

891 if not self.ignore_unknown_fields: 

892 raise 

893 

894 def _ConvertAndSetScalarToMapKey( 

895 self, message, map_field, converted_key, js_value, path 

896 ): 

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

898 try: 

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

900 _ConvertScalarFieldValue( 

901 js_value, 

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

903 path, 

904 ) 

905 ) 

906 except EnumStringValueParseError: 

907 if not self.ignore_unknown_fields: 

908 raise 

909 

910 

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

912 """Convert a single scalar field value. 

913 

914 Args: 

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

916 field: The descriptor of the field to convert. 

917 path: parent path to log parse error info. 

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

919 

920 Returns: 

921 The converted scalar field value 

922 

923 Raises: 

924 ParseError: In case of convert problems. 

925 EnumStringValueParseError: In case of unknown enum string value. 

926 """ 

927 try: 

928 if field.cpp_type in _INT_TYPES: 

929 return _ConvertInteger(value) 

930 elif field.cpp_type in _FLOAT_TYPES: 

931 return _ConvertFloat(value, field) 

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

933 return _ConvertBool(value, require_str) 

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

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

936 if isinstance(value, str): 

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

938 else: 

939 encoded = value 

940 # Add extra padding '=' 

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

942 return base64.urlsafe_b64decode(padded_value) 

943 else: 

944 # Checking for unpaired surrogates appears to be unreliable, 

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

946 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

947 raise ParseError('Unpaired surrogate') 

948 return value 

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

950 # Convert an enum value. 

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

952 if enum_value is None: 

953 try: 

954 number = int(value) 

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

956 except ValueError as e: 

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

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

959 raise EnumStringValueParseError( 

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

961 value, field.enum_type.full_name 

962 ) 

963 ) from e 

964 if enum_value is None: 

965 if field.enum_type.is_closed: 

966 raise ParseError( 

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

968 value, field.enum_type.full_name 

969 ) 

970 ) 

971 else: 

972 return number 

973 return enum_value.number 

974 except EnumStringValueParseError as e: 

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

976 except ParseError as e: 

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

978 

979 

980def _ConvertInteger(value): 

981 """Convert an integer. 

982 

983 Args: 

984 value: A scalar value to convert. 

985 

986 Returns: 

987 The integer value. 

988 

989 Raises: 

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

991 """ 

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

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

994 

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

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

997 

998 if isinstance(value, bool): 

999 raise ParseError( 

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

1001 ) 

1002 

1003 try: 

1004 return int(value) 

1005 except ValueError as e: 

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

1007 try: 

1008 f = float(value) 

1009 except ValueError: 

1010 # Raise the original exception for the int parse. 

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

1012 if not f.is_integer(): 

1013 raise ParseError( 

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

1015 ) from e 

1016 return int(f) 

1017 

1018 

1019def _ConvertFloat(value, field): 

1020 """Convert an floating point number.""" 

1021 if isinstance(value, float): 

1022 if math.isnan(value): 

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

1024 if math.isinf(value): 

1025 if value > 0: 

1026 raise ParseError( 

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

1028 'use quoted "Infinity" instead' 

1029 ) 

1030 else: 

1031 raise ParseError( 

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

1033 'use quoted "-Infinity" instead' 

1034 ) 

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

1036 # pylint: disable=protected-access 

1037 if value > type_checkers._FLOAT_MAX: 

1038 raise ParseError('Float value too large') 

1039 # pylint: disable=protected-access 

1040 if value < type_checkers._FLOAT_MIN: 

1041 raise ParseError('Float value too small') 

1042 if value == 'nan': 

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

1044 try: 

1045 # Assume Python compatible syntax. 

1046 return float(value) 

1047 except ValueError as e: 

1048 # Check alternative spellings. 

1049 if value == _NEG_INFINITY: 

1050 return float('-inf') 

1051 elif value == _INFINITY: 

1052 return float('inf') 

1053 elif value == _NAN: 

1054 return float('nan') 

1055 else: 

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

1057 

1058 

1059def _ConvertBool(value, require_str): 

1060 """Convert a boolean value. 

1061 

1062 Args: 

1063 value: A scalar value to convert. 

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

1065 

1066 Returns: 

1067 The bool parsed. 

1068 

1069 Raises: 

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

1071 """ 

1072 if require_str: 

1073 if value == 'true': 

1074 return True 

1075 elif value == 'false': 

1076 return False 

1077 else: 

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

1079 

1080 if not isinstance(value, bool): 

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

1082 return value 

1083 

1084 

1085_WKTJSONMETHODS = { 

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

1087 'google.protobuf.Duration': [ 

1088 '_GenericMessageToJsonObject', 

1089 '_ConvertGenericMessage', 

1090 ], 

1091 'google.protobuf.FieldMask': [ 

1092 '_GenericMessageToJsonObject', 

1093 '_ConvertGenericMessage', 

1094 ], 

1095 'google.protobuf.ListValue': [ 

1096 '_ListValueMessageToJsonObject', 

1097 '_ConvertListOrTupleValueMessage', 

1098 ], 

1099 'google.protobuf.Struct': [ 

1100 '_StructMessageToJsonObject', 

1101 '_ConvertStructMessage', 

1102 ], 

1103 'google.protobuf.Timestamp': [ 

1104 '_GenericMessageToJsonObject', 

1105 '_ConvertGenericMessage', 

1106 ], 

1107 'google.protobuf.Value': [ 

1108 '_ValueMessageToJsonObject', 

1109 '_ConvertValueMessage', 

1110 ], 

1111}