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

494 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 

29import warnings 

30 

31from google.protobuf import descriptor 

32from google.protobuf import message_factory 

33from google.protobuf import symbol_database 

34from google.protobuf.internal import type_checkers 

35 

36 

37_INT_TYPES = frozenset([ 

38 descriptor.FieldDescriptor.CPPTYPE_INT32, 

39 descriptor.FieldDescriptor.CPPTYPE_UINT32, 

40 descriptor.FieldDescriptor.CPPTYPE_INT64, 

41 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

42]) 

43_INT64_TYPES = frozenset([ 

44 descriptor.FieldDescriptor.CPPTYPE_INT64, 

45 descriptor.FieldDescriptor.CPPTYPE_UINT64, 

46]) 

47_FLOAT_TYPES = frozenset([ 

48 descriptor.FieldDescriptor.CPPTYPE_FLOAT, 

49 descriptor.FieldDescriptor.CPPTYPE_DOUBLE, 

50]) 

51_INFINITY = 'Infinity' 

52_NEG_INFINITY = '-Infinity' 

53_NAN = 'NaN' 

54 

55_UNPAIRED_SURROGATE_PATTERN = re.compile( 

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

57) 

58 

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

60 

61 

62class Error(Exception): 

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

64 

65 

66class SerializeToJsonError(Error): 

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

68 

69 

70class ParseError(Error): 

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

72 

73 

74class EnumStringValueParseError(ParseError): 

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

76 

77 This exception is suppressed if ignore_unknown_fields is set. 

78 """ 

79 

80 

81def MessageToJson( 

82 message, 

83 preserving_proto_field_name=False, 

84 indent=2, 

85 sort_keys=False, 

86 use_integers_for_enums=False, 

87 descriptor_pool=None, 

88 float_precision=None, 

89 ensure_ascii=True, 

90 always_print_fields_with_no_presence=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 float_precision: Deprecated. If set, use this to specify float field valid 

111 digits. 

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

113 False, Unicode strings are returned unchanged. 

114 

115 Returns: 

116 A string containing the JSON formatted protocol buffer message. 

117 """ 

118 printer = _Printer( 

119 preserving_proto_field_name, 

120 use_integers_for_enums, 

121 descriptor_pool, 

122 float_precision, 

123 always_print_fields_with_no_presence, 

124 ) 

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

126 

127 

128def MessageToDict( 

129 message, 

130 always_print_fields_with_no_presence=False, 

131 preserving_proto_field_name=False, 

132 use_integers_for_enums=False, 

133 descriptor_pool=None, 

134 float_precision=None, 

135): 

136 """Converts protobuf message to a dictionary. 

137 

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

139 

140 Args: 

141 message: The protocol buffers message instance to serialize. 

142 always_print_fields_with_no_presence: If True, fields without presence 

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

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

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

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

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

148 lowerCamelCase. 

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

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

151 default. 

152 float_precision: Deprecated. If set, use this to specify float field valid 

153 digits. 

154 

155 Returns: 

156 A dict representation of the protocol buffer message. 

157 """ 

158 printer = _Printer( 

159 preserving_proto_field_name, 

160 use_integers_for_enums, 

161 descriptor_pool, 

162 float_precision, 

163 always_print_fields_with_no_presence, 

164 ) 

165 # pylint: disable=protected-access 

166 return printer._MessageToJsonObject(message) 

167 

168 

169def _IsMapEntry(field): 

170 return ( 

171 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE 

172 and field.message_type.has_options 

173 and field.message_type.GetOptions().map_entry 

174 ) 

175 

176 

177class _Printer(object): 

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

179 

180 def __init__( 

181 self, 

182 preserving_proto_field_name=False, 

183 use_integers_for_enums=False, 

184 descriptor_pool=None, 

185 float_precision=None, 

186 always_print_fields_with_no_presence=False, 

187 ): 

188 self.always_print_fields_with_no_presence = ( 

189 always_print_fields_with_no_presence 

190 ) 

191 self.preserving_proto_field_name = preserving_proto_field_name 

192 self.use_integers_for_enums = use_integers_for_enums 

193 self.descriptor_pool = descriptor_pool 

194 if float_precision: 

195 warnings.warn( 

196 'float_precision option is deprecated for json_format. ' 

197 'This will turn into error in 7.34.0, please remove it ' 

198 'before that.' 

199 ) 

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

201 else: 

202 self.float_format = None 

203 

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

205 js = self._MessageToJsonObject(message) 

206 return json.dumps( 

207 js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii 

208 ) 

209 

210 def _MessageToJsonObject(self, message): 

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

212 message_descriptor = message.DESCRIPTOR 

213 full_name = message_descriptor.full_name 

214 if _IsWrapperMessage(message_descriptor): 

215 return self._WrapperMessageToJsonObject(message) 

216 if full_name in _WKTJSONMETHODS: 

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

218 js = {} 

219 return self._RegularMessageToJsonObject(message, js) 

220 

221 def _RegularMessageToJsonObject(self, message, js): 

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

223 fields = message.ListFields() 

224 

225 try: 

226 for field, value in fields: 

227 if field.is_extension: 

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

229 elif self.preserving_proto_field_name: 

230 name = field.name 

231 else: 

232 name = field.json_name 

233 

234 if _IsMapEntry(field): 

235 # Convert a map field. 

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

237 js_map = {} 

238 for key in value: 

239 if isinstance(key, bool): 

240 if key: 

241 recorded_key = 'true' 

242 else: 

243 recorded_key = 'false' 

244 else: 

245 recorded_key = str(key) 

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

247 js[name] = js_map 

248 elif field.is_repeated: 

249 # Convert a repeated field. 

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

251 else: 

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

253 

254 # Serialize default value if including_default_value_fields is True. 

255 if ( 

256 self.always_print_fields_with_no_presence 

257 ): 

258 message_descriptor = message.DESCRIPTOR 

259 for field in message_descriptor.fields: 

260 

261 # always_print_fields_with_no_presence doesn't apply to 

262 # any field which supports presence. 

263 if self.always_print_fields_with_no_presence and field.has_presence: 

264 continue 

265 

266 if self.preserving_proto_field_name: 

267 name = field.name 

268 else: 

269 name = field.json_name 

270 if name in js: 

271 # Skip the field which has been serialized already. 

272 continue 

273 if _IsMapEntry(field): 

274 js[name] = {} 

275 elif field.is_repeated: 

276 js[name] = [] 

277 else: 

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

279 

280 except ValueError as e: 

281 raise SerializeToJsonError( 

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

283 ) from e 

284 

285 return js 

286 

287 def _FieldToJsonObject(self, field, value): 

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

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

290 return self._MessageToJsonObject(value) 

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

292 if self.use_integers_for_enums: 

293 return value 

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

295 return None 

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

297 if enum_value is not None: 

298 return enum_value.name 

299 else: 

300 if field.enum_type.is_closed: 

301 raise SerializeToJsonError( 

302 'Enum field contains an integer value ' 

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

304 ) 

305 else: 

306 return value 

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

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

309 # Use base64 Data encoding for bytes 

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

311 else: 

312 return str(value) 

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

314 return bool(value) 

315 elif field.cpp_type in _INT64_TYPES: 

316 return str(value) 

317 elif field.cpp_type in _FLOAT_TYPES: 

318 if math.isinf(value): 

319 if value < 0.0: 

320 return _NEG_INFINITY 

321 else: 

322 return _INFINITY 

323 if math.isnan(value): 

324 return _NAN 

325 if self.float_format: 

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

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

328 return type_checkers.ToShortestFloat(value) 

329 

330 return value 

331 

332 def _AnyMessageToJsonObject(self, message): 

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

334 if not message.ListFields(): 

335 return {} 

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

337 js = OrderedDict() 

338 type_url = message.type_url 

339 js['@type'] = type_url 

340 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

341 sub_message.ParseFromString(message.value) 

342 message_descriptor = sub_message.DESCRIPTOR 

343 full_name = message_descriptor.full_name 

344 if _IsWrapperMessage(message_descriptor): 

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

346 return js 

347 if full_name in _WKTJSONMETHODS: 

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

349 self 

350 ) 

351 return js 

352 return self._RegularMessageToJsonObject(sub_message, js) 

353 

354 def _GenericMessageToJsonObject(self, message): 

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

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

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

358 return message.ToJsonString() 

359 

360 def _ValueMessageToJsonObject(self, message): 

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

362 which = message.WhichOneof('kind') 

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

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

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

366 return None 

367 if which == 'list_value': 

368 return self._ListValueMessageToJsonObject(message.list_value) 

369 if which == 'number_value': 

370 value = message.number_value 

371 if math.isinf(value): 

372 raise ValueError( 

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

374 'which would parse as string_value' 

375 ) 

376 if math.isnan(value): 

377 raise ValueError( 

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

379 'which would parse as string_value' 

380 ) 

381 else: 

382 value = getattr(message, which) 

383 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 

384 return self._FieldToJsonObject(oneof_descriptor, value) 

385 

386 def _ListValueMessageToJsonObject(self, message): 

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

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

389 

390 def _StructMessageToJsonObject(self, message): 

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

392 fields = message.fields 

393 ret = {} 

394 for key in fields: 

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

396 return ret 

397 

398 def _WrapperMessageToJsonObject(self, message): 

399 return self._FieldToJsonObject( 

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

401 ) 

402 

403 

404def _IsWrapperMessage(message_descriptor): 

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

406 

407 

408def _DuplicateChecker(js): 

409 result = {} 

410 for name, value in js: 

411 if name in result: 

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

413 result[name] = value 

414 return result 

415 

416 

417def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 

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

419 db = symbol_database.Default() 

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

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

422 try: 

423 message_descriptor = pool.FindMessageTypeByName(type_name) 

424 except KeyError as e: 

425 raise TypeError( 

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

427 ) from e 

428 message_class = message_factory.GetMessageClass(message_descriptor) 

429 return message_class() 

430 

431 

432def Parse( 

433 text, 

434 message, 

435 ignore_unknown_fields=False, 

436 descriptor_pool=None, 

437 max_recursion_depth=100, 

438): 

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

440 

441 Args: 

442 text: Message JSON representation. 

443 message: A protocol buffer message to merge into. 

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

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

446 default. 

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

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

449 is 100. 

450 

451 Returns: 

452 The same message passed as argument. 

453 

454 Raises:: 

455 ParseError: On JSON parsing problems. 

456 """ 

457 if not isinstance(text, str): 

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

459 

460 try: 

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

462 except Exception as e: 

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

464 

465 try: 

466 return ParseDict( 

467 js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

468 ) 

469 except ParseError as e: 

470 raise e 

471 except Exception as e: 

472 raise ParseError( 

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

474 ) from e 

475 

476 

477def ParseDict( 

478 js_dict, 

479 message, 

480 ignore_unknown_fields=False, 

481 descriptor_pool=None, 

482 max_recursion_depth=100, 

483): 

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

485 

486 Args: 

487 js_dict: Dict representation of a JSON message. 

488 message: A protocol buffer message to merge into. 

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

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

491 default. 

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

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

494 is 100. 

495 

496 Returns: 

497 The same message passed as argument. 

498 """ 

499 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

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

501 return message 

502 

503 

504_INT_OR_FLOAT = (int, float) 

505_LIST_LIKE = (list, tuple) 

506 

507 

508class _Parser(object): 

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

510 

511 def __init__( 

512 self, ignore_unknown_fields, descriptor_pool, max_recursion_depth 

513 ): 

514 self.ignore_unknown_fields = ignore_unknown_fields 

515 self.descriptor_pool = descriptor_pool 

516 self.max_recursion_depth = max_recursion_depth 

517 self.recursion_depth = 0 

518 

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

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

521 

522 Args: 

523 value: A JSON object. 

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

525 path: parent path to log parse error info. 

526 

527 Raises: 

528 ParseError: In case of convert problems. 

529 """ 

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

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

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

533 # allowed, but attempting depth 5 raises ParseError. 

534 self.recursion_depth += 1 

535 if self.recursion_depth > self.max_recursion_depth: 

536 raise ParseError( 

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

538 self.max_recursion_depth 

539 ) 

540 ) 

541 message_descriptor = message.DESCRIPTOR 

542 full_name = message_descriptor.full_name 

543 if not path: 

544 path = message_descriptor.name 

545 if _IsWrapperMessage(message_descriptor): 

546 self._ConvertWrapperMessage(value, message, path) 

547 elif full_name in _WKTJSONMETHODS: 

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

549 else: 

550 self._ConvertFieldValuePair(value, message, path) 

551 self.recursion_depth -= 1 

552 

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

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

555 

556 Args: 

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

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

559 path: parent path to log parse error info. 

560 

561 Raises: 

562 ParseError: In case of problems converting. 

563 """ 

564 names = [] 

565 message_descriptor = message.DESCRIPTOR 

566 fields_by_json_name = dict( 

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

568 ) 

569 

570 def _ClearFieldOrExtension(message, field): 

571 if field.is_extension: 

572 message.ClearExtension(field) 

573 else: 

574 message.ClearField(field.name) 

575 

576 def _GetFieldOrExtension(message, field): 

577 if field.is_extension: 

578 return message.Extensions[field] 

579 else: 

580 return getattr(message, field.name) 

581 

582 def _SetFieldOrExtension(message, field, value): 

583 if field.is_extension: 

584 message.Extensions[field] = value 

585 else: 

586 setattr(message, field.name, value) 

587 

588 for name in js: 

589 try: 

590 field = fields_by_json_name.get(name, None) 

591 if not field: 

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

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

594 if not message_descriptor.is_extendable: 

595 raise ParseError( 

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

597 message_descriptor.full_name, path 

598 ) 

599 ) 

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

601 # pylint: disable=protected-access 

602 field = message.Extensions._FindExtensionByName(identifier) 

603 # pylint: enable=protected-access 

604 if not field: 

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

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

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

608 # pylint: disable=protected-access 

609 field = message.Extensions._FindExtensionByName(identifier) 

610 # pylint: enable=protected-access 

611 if not field: 

612 if self.ignore_unknown_fields: 

613 continue 

614 raise ParseError( 

615 ( 

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

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

618 ).format( 

619 message_descriptor.full_name, 

620 name, 

621 path, 

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

623 ) 

624 ) 

625 if name in names: 

626 raise ParseError( 

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

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

629 message.DESCRIPTOR.full_name, name, path 

630 ) 

631 ) 

632 names.append(name) 

633 value = js[name] 

634 # Check no other oneof field is parsed. 

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

636 oneof_name = field.containing_oneof.name 

637 if oneof_name in names: 

638 raise ParseError( 

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

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

641 message.DESCRIPTOR.full_name, oneof_name, path 

642 ) 

643 ) 

644 names.append(oneof_name) 

645 

646 if value is None: 

647 if ( 

648 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

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

650 ): 

651 sub_message = _GetFieldOrExtension(message, field) 

652 sub_message.null_value = 0 

653 elif ( 

654 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

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

656 ): 

657 _SetFieldOrExtension(message, field, 0) 

658 else: 

659 _ClearFieldOrExtension(message, field) 

660 continue 

661 

662 # Parse field value. 

663 if _IsMapEntry(field): 

664 _ClearFieldOrExtension(message, field) 

665 self._ConvertMapFieldValue( 

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

667 ) 

668 elif field.is_repeated: 

669 _ClearFieldOrExtension(message, field) 

670 if not isinstance(value, _LIST_LIKE): 

671 raise ParseError( 

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

673 name, value, path 

674 ) 

675 ) 

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

677 # Repeated message field. 

678 for index, item in enumerate(value): 

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

680 # None is a null_value in Value. 

681 if ( 

682 item is None 

683 and sub_message.DESCRIPTOR.full_name 

684 != 'google.protobuf.Value' 

685 ): 

686 raise ParseError( 

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

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

689 path, name, index 

690 ) 

691 ) 

692 self.ConvertMessage( 

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

694 ) 

695 else: 

696 # Repeated scalar field. 

697 for index, item in enumerate(value): 

698 if item is None: 

699 raise ParseError( 

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

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

702 path, name, index 

703 ) 

704 ) 

705 self._ConvertAndAppendScalar( 

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

707 ) 

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

709 sub_message = _GetFieldOrExtension(message, field) 

710 sub_message.SetInParent() 

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

712 else: 

713 self._ConvertAndSetScalar( 

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

715 ) 

716 except ParseError as e: 

717 if field and field.containing_oneof is None: 

718 raise ParseError( 

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

720 ) from e 

721 else: 

722 raise ParseError(str(e)) from e 

723 except ValueError as e: 

724 raise ParseError( 

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

726 ) from e 

727 except TypeError as e: 

728 raise ParseError( 

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

730 ) from e 

731 

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

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

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

735 return 

736 try: 

737 type_url = value['@type'] 

738 except KeyError as e: 

739 raise ParseError( 

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

741 ) from e 

742 

743 try: 

744 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

745 except TypeError as e: 

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

747 message_descriptor = sub_message.DESCRIPTOR 

748 full_name = message_descriptor.full_name 

749 if _IsWrapperMessage(message_descriptor): 

750 self._ConvertWrapperMessage( 

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

752 ) 

753 elif full_name in _WKTJSONMETHODS: 

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

755 # to ensure recursion depth is properly tracked 

756 self.ConvertMessage( 

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

758 ) 

759 else: 

760 del value['@type'] 

761 try: 

762 self._ConvertFieldValuePair(value, sub_message, path) 

763 finally: 

764 value['@type'] = type_url 

765 # Sets Any message 

766 message.value = sub_message.SerializeToString() 

767 message.type_url = type_url 

768 

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

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

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

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

773 try: 

774 message.FromJsonString(value) 

775 except ValueError as e: 

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

777 

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

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

780 if isinstance(value, dict): 

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

782 elif isinstance(value, _LIST_LIKE): 

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

784 elif value is None: 

785 message.null_value = 0 

786 elif isinstance(value, bool): 

787 message.bool_value = value 

788 elif isinstance(value, str): 

789 message.string_value = value 

790 elif isinstance(value, _INT_OR_FLOAT): 

791 message.number_value = value 

792 else: 

793 raise ParseError( 

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

795 value, type(value), path 

796 ) 

797 ) 

798 

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

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

801 if not isinstance(value, _LIST_LIKE): 

802 raise ParseError( 

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

804 ) 

805 message.ClearField('values') 

806 for index, item in enumerate(value): 

807 self._ConvertValueMessage( 

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

809 ) 

810 

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

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

813 if not isinstance(value, dict): 

814 raise ParseError( 

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

816 ) 

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

818 # there are no values. 

819 message.Clear() 

820 for key in value: 

821 self._ConvertValueMessage( 

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

823 ) 

824 return 

825 

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

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

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

829 self._ConvertAndSetScalar( 

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

831 ) 

832 

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

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

835 

836 Args: 

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

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

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

840 path: parent path to log parse error info. 

841 

842 Raises: 

843 ParseError: In case of convert problems. 

844 """ 

845 if not isinstance(value, dict): 

846 raise ParseError( 

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

848 field.name, value, path 

849 ) 

850 ) 

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

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

853 for key in value: 

854 key_value = _ConvertScalarFieldValue( 

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

856 ) 

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

858 self.ConvertMessage( 

859 value[key], 

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

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

862 ) 

863 else: 

864 self._ConvertAndSetScalarToMapKey( 

865 message, 

866 field, 

867 key_value, 

868 value[key], 

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

870 ) 

871 

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

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

874 try: 

875 value = _ConvertScalarFieldValue(js_value, field, path) 

876 if field.is_extension: 

877 message.Extensions[field] = value 

878 else: 

879 setattr(message, field.name, value) 

880 except EnumStringValueParseError: 

881 if not self.ignore_unknown_fields: 

882 raise 

883 

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

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

886 try: 

887 if repeated_field.is_extension: 

888 repeated = message.Extensions[repeated_field] 

889 else: 

890 repeated = getattr(message, repeated_field.name) 

891 value = _ConvertScalarFieldValue(js_value, repeated_field, path) 

892 repeated.append(value) 

893 except EnumStringValueParseError: 

894 if not self.ignore_unknown_fields: 

895 raise 

896 

897 def _ConvertAndSetScalarToMapKey( 

898 self, message, map_field, converted_key, js_value, path 

899 ): 

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

901 try: 

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

903 _ConvertScalarFieldValue( 

904 js_value, 

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

906 path, 

907 ) 

908 ) 

909 except EnumStringValueParseError: 

910 if not self.ignore_unknown_fields: 

911 raise 

912 

913 

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

915 """Convert a single scalar field value. 

916 

917 Args: 

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

919 field: The descriptor of the field to convert. 

920 path: parent path to log parse error info. 

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

922 

923 Returns: 

924 The converted scalar field value 

925 

926 Raises: 

927 ParseError: In case of convert problems. 

928 EnumStringValueParseError: In case of unknown enum string value. 

929 """ 

930 try: 

931 if field.cpp_type in _INT_TYPES: 

932 return _ConvertInteger(value) 

933 elif field.cpp_type in _FLOAT_TYPES: 

934 return _ConvertFloat(value, field) 

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

936 return _ConvertBool(value, require_str) 

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

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

939 if isinstance(value, str): 

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

941 else: 

942 encoded = value 

943 # Add extra padding '=' 

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

945 return base64.urlsafe_b64decode(padded_value) 

946 else: 

947 # Checking for unpaired surrogates appears to be unreliable, 

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

949 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

950 raise ParseError('Unpaired surrogate') 

951 return value 

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

953 # Convert an enum value. 

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

955 if enum_value is None: 

956 try: 

957 number = int(value) 

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

959 except ValueError as e: 

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

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

962 raise EnumStringValueParseError( 

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

964 value, field.enum_type.full_name 

965 ) 

966 ) from e 

967 if enum_value is None: 

968 if field.enum_type.is_closed: 

969 raise ParseError( 

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

971 value, field.enum_type.full_name 

972 ) 

973 ) 

974 else: 

975 return number 

976 return enum_value.number 

977 except EnumStringValueParseError as e: 

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

979 except ParseError as e: 

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

981 

982 

983def _ConvertInteger(value): 

984 """Convert an integer. 

985 

986 Args: 

987 value: A scalar value to convert. 

988 

989 Returns: 

990 The integer value. 

991 

992 Raises: 

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

994 """ 

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

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

997 

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

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

1000 

1001 if isinstance(value, bool): 

1002 raise ParseError( 

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

1004 ) 

1005 

1006 try: 

1007 return int(value) 

1008 except ValueError as e: 

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

1010 try: 

1011 f = float(value) 

1012 except ValueError: 

1013 # Raise the original exception for the int parse. 

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

1015 if not f.is_integer(): 

1016 raise ParseError( 

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

1018 ) from e 

1019 return int(f) 

1020 

1021 

1022def _ConvertFloat(value, field): 

1023 """Convert an floating point number.""" 

1024 if isinstance(value, float): 

1025 if math.isnan(value): 

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

1027 if math.isinf(value): 

1028 if value > 0: 

1029 raise ParseError( 

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

1031 'use quoted "Infinity" instead' 

1032 ) 

1033 else: 

1034 raise ParseError( 

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

1036 'use quoted "-Infinity" instead' 

1037 ) 

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

1039 # pylint: disable=protected-access 

1040 if value > type_checkers._FLOAT_MAX: 

1041 raise ParseError('Float value too large') 

1042 # pylint: disable=protected-access 

1043 if value < type_checkers._FLOAT_MIN: 

1044 raise ParseError('Float value too small') 

1045 if value == 'nan': 

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

1047 try: 

1048 # Assume Python compatible syntax. 

1049 return float(value) 

1050 except ValueError as e: 

1051 # Check alternative spellings. 

1052 if value == _NEG_INFINITY: 

1053 return float('-inf') 

1054 elif value == _INFINITY: 

1055 return float('inf') 

1056 elif value == _NAN: 

1057 return float('nan') 

1058 else: 

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

1060 

1061 

1062def _ConvertBool(value, require_str): 

1063 """Convert a boolean value. 

1064 

1065 Args: 

1066 value: A scalar value to convert. 

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

1068 

1069 Returns: 

1070 The bool parsed. 

1071 

1072 Raises: 

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

1074 """ 

1075 if require_str: 

1076 if value == 'true': 

1077 return True 

1078 elif value == 'false': 

1079 return False 

1080 else: 

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

1082 

1083 if not isinstance(value, bool): 

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

1085 return value 

1086 

1087 

1088_WKTJSONMETHODS = { 

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

1090 'google.protobuf.Duration': [ 

1091 '_GenericMessageToJsonObject', 

1092 '_ConvertGenericMessage', 

1093 ], 

1094 'google.protobuf.FieldMask': [ 

1095 '_GenericMessageToJsonObject', 

1096 '_ConvertGenericMessage', 

1097 ], 

1098 'google.protobuf.ListValue': [ 

1099 '_ListValueMessageToJsonObject', 

1100 '_ConvertListOrTupleValueMessage', 

1101 ], 

1102 'google.protobuf.Struct': [ 

1103 '_StructMessageToJsonObject', 

1104 '_ConvertStructMessage', 

1105 ], 

1106 'google.protobuf.Timestamp': [ 

1107 '_GenericMessageToJsonObject', 

1108 '_ConvertGenericMessage', 

1109 ], 

1110 'google.protobuf.Value': [ 

1111 '_ValueMessageToJsonObject', 

1112 '_ConvertValueMessage', 

1113 ], 

1114}