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

440 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:45 +0000

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.internal import type_checkers 

31from google.protobuf import descriptor 

32from google.protobuf import message_factory 

33from google.protobuf import symbol_database 

34 

35 

36_INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32, 

37 descriptor.FieldDescriptor.CPPTYPE_UINT32, 

38 descriptor.FieldDescriptor.CPPTYPE_INT64, 

39 descriptor.FieldDescriptor.CPPTYPE_UINT64]) 

40_INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64, 

41 descriptor.FieldDescriptor.CPPTYPE_UINT64]) 

42_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT, 

43 descriptor.FieldDescriptor.CPPTYPE_DOUBLE]) 

44_INFINITY = 'Infinity' 

45_NEG_INFINITY = '-Infinity' 

46_NAN = 'NaN' 

47 

48_UNPAIRED_SURROGATE_PATTERN = re.compile( 

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

50 

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

52 

53 

54class Error(Exception): 

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

56 

57 

58class SerializeToJsonError(Error): 

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

60 

61 

62class ParseError(Error): 

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

64 

65 

66def MessageToJson( 

67 message, 

68 including_default_value_fields=False, 

69 preserving_proto_field_name=False, 

70 indent=2, 

71 sort_keys=False, 

72 use_integers_for_enums=False, 

73 descriptor_pool=None, 

74 float_precision=None, 

75 ensure_ascii=True): 

76 """Converts protobuf message to JSON format. 

77 

78 Args: 

79 message: The protocol buffers message instance to serialize. 

80 including_default_value_fields: If True, singular primitive fields, 

81 repeated fields, and map fields will always be serialized. If 

82 False, only serialize non-empty fields. Singular message fields 

83 and oneof fields are not affected by this option. 

84 preserving_proto_field_name: If True, use the original proto field 

85 names as defined in the .proto file. If False, convert the field 

86 names to lowerCamelCase. 

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

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

89 indent level is None, no newlines will be inserted. 

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

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

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

93 default. 

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

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

96 If False, Unicode strings are returned unchanged. 

97 

98 Returns: 

99 A string containing the JSON formatted protocol buffer message. 

100 """ 

101 printer = _Printer( 

102 including_default_value_fields, 

103 preserving_proto_field_name, 

104 use_integers_for_enums, 

105 descriptor_pool, 

106 float_precision=float_precision) 

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

108 

109 

110def MessageToDict( 

111 message, 

112 including_default_value_fields=False, 

113 preserving_proto_field_name=False, 

114 use_integers_for_enums=False, 

115 descriptor_pool=None, 

116 float_precision=None): 

117 """Converts protobuf message to a dictionary. 

118 

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

120 

121 Args: 

122 message: The protocol buffers message instance to serialize. 

123 including_default_value_fields: If True, singular primitive fields, 

124 repeated fields, and map fields will always be serialized. If 

125 False, only serialize non-empty fields. Singular message fields 

126 and oneof fields are not affected by this option. 

127 preserving_proto_field_name: If True, use the original proto field 

128 names as defined in the .proto file. If False, convert the field 

129 names to lowerCamelCase. 

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

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

132 default. 

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

134 

135 Returns: 

136 A dict representation of the protocol buffer message. 

137 """ 

138 printer = _Printer( 

139 including_default_value_fields, 

140 preserving_proto_field_name, 

141 use_integers_for_enums, 

142 descriptor_pool, 

143 float_precision=float_precision) 

144 # pylint: disable=protected-access 

145 return printer._MessageToJsonObject(message) 

146 

147 

148def _IsMapEntry(field): 

149 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and 

150 field.message_type.has_options and 

151 field.message_type.GetOptions().map_entry) 

152 

153 

154class _Printer(object): 

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

156 

157 def __init__( 

158 self, 

159 including_default_value_fields=False, 

160 preserving_proto_field_name=False, 

161 use_integers_for_enums=False, 

162 descriptor_pool=None, 

163 float_precision=None): 

164 self.including_default_value_fields = including_default_value_fields 

165 self.preserving_proto_field_name = preserving_proto_field_name 

166 self.use_integers_for_enums = use_integers_for_enums 

167 self.descriptor_pool = descriptor_pool 

168 if float_precision: 

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

170 else: 

171 self.float_format = None 

172 

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

174 js = self._MessageToJsonObject(message) 

175 return json.dumps( 

176 js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii) 

177 

178 def _MessageToJsonObject(self, message): 

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

180 message_descriptor = message.DESCRIPTOR 

181 full_name = message_descriptor.full_name 

182 if _IsWrapperMessage(message_descriptor): 

183 return self._WrapperMessageToJsonObject(message) 

184 if full_name in _WKTJSONMETHODS: 

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

186 js = {} 

187 return self._RegularMessageToJsonObject(message, js) 

188 

189 def _RegularMessageToJsonObject(self, message, js): 

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

191 fields = message.ListFields() 

192 

193 try: 

194 for field, value in fields: 

195 if self.preserving_proto_field_name: 

196 name = field.name 

197 else: 

198 name = field.json_name 

199 if _IsMapEntry(field): 

200 # Convert a map field. 

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

202 js_map = {} 

203 for key in value: 

204 if isinstance(key, bool): 

205 if key: 

206 recorded_key = 'true' 

207 else: 

208 recorded_key = 'false' 

209 else: 

210 recorded_key = str(key) 

211 js_map[recorded_key] = self._FieldToJsonObject( 

212 v_field, value[key]) 

213 js[name] = js_map 

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

215 # Convert a repeated field. 

216 js[name] = [self._FieldToJsonObject(field, k) 

217 for k in value] 

218 elif field.is_extension: 

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

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

221 else: 

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

223 

224 # Serialize default value if including_default_value_fields is True. 

225 if self.including_default_value_fields: 

226 message_descriptor = message.DESCRIPTOR 

227 for field in message_descriptor.fields: 

228 # Singular message fields and oneof fields will not be affected. 

229 if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and 

230 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or 

231 field.containing_oneof): 

232 continue 

233 if self.preserving_proto_field_name: 

234 name = field.name 

235 else: 

236 name = field.json_name 

237 if name in js: 

238 # Skip the field which has been serialized already. 

239 continue 

240 if _IsMapEntry(field): 

241 js[name] = {} 

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

243 js[name] = [] 

244 else: 

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

246 

247 except ValueError as e: 

248 raise SerializeToJsonError( 

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

250 

251 return js 

252 

253 def _FieldToJsonObject(self, field, value): 

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

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

256 return self._MessageToJsonObject(value) 

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

258 if self.use_integers_for_enums: 

259 return value 

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

261 return None 

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

263 if enum_value is not None: 

264 return enum_value.name 

265 else: 

266 if field.enum_type.is_closed: 

267 raise SerializeToJsonError('Enum field contains an integer value ' 

268 'which can not mapped to an enum value.') 

269 else: 

270 return value 

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

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

273 # Use base64 Data encoding for bytes 

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

275 else: 

276 return value 

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

278 return bool(value) 

279 elif field.cpp_type in _INT64_TYPES: 

280 return str(value) 

281 elif field.cpp_type in _FLOAT_TYPES: 

282 if math.isinf(value): 

283 if value < 0.0: 

284 return _NEG_INFINITY 

285 else: 

286 return _INFINITY 

287 if math.isnan(value): 

288 return _NAN 

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

290 if self.float_format: 

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

292 else: 

293 return type_checkers.ToShortestFloat(value) 

294 

295 return value 

296 

297 def _AnyMessageToJsonObject(self, message): 

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

299 if not message.ListFields(): 

300 return {} 

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

302 js = OrderedDict() 

303 type_url = message.type_url 

304 js['@type'] = type_url 

305 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

306 sub_message.ParseFromString(message.value) 

307 message_descriptor = sub_message.DESCRIPTOR 

308 full_name = message_descriptor.full_name 

309 if _IsWrapperMessage(message_descriptor): 

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

311 return js 

312 if full_name in _WKTJSONMETHODS: 

313 js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0], 

314 sub_message)(self) 

315 return js 

316 return self._RegularMessageToJsonObject(sub_message, js) 

317 

318 def _GenericMessageToJsonObject(self, message): 

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

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

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

322 return message.ToJsonString() 

323 

324 def _ValueMessageToJsonObject(self, message): 

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

326 which = message.WhichOneof('kind') 

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

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

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

330 return None 

331 if which == 'list_value': 

332 return self._ListValueMessageToJsonObject(message.list_value) 

333 if which == 'number_value': 

334 value = message.number_value 

335 if math.isinf(value): 

336 raise ValueError('Fail to serialize Infinity for Value.number_value, ' 

337 'which would parse as string_value') 

338 if math.isnan(value): 

339 raise ValueError('Fail to serialize NaN for Value.number_value, ' 

340 'which would parse as string_value') 

341 else: 

342 value = getattr(message, which) 

343 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 

344 return self._FieldToJsonObject(oneof_descriptor, value) 

345 

346 def _ListValueMessageToJsonObject(self, message): 

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

348 return [self._ValueMessageToJsonObject(value) 

349 for value in message.values] 

350 

351 def _StructMessageToJsonObject(self, message): 

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

353 fields = message.fields 

354 ret = {} 

355 for key in fields: 

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

357 return ret 

358 

359 def _WrapperMessageToJsonObject(self, message): 

360 return self._FieldToJsonObject( 

361 message.DESCRIPTOR.fields_by_name['value'], message.value) 

362 

363 

364def _IsWrapperMessage(message_descriptor): 

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

366 

367 

368def _DuplicateChecker(js): 

369 result = {} 

370 for name, value in js: 

371 if name in result: 

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

373 result[name] = value 

374 return result 

375 

376 

377def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 

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

379 db = symbol_database.Default() 

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

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

382 try: 

383 message_descriptor = pool.FindMessageTypeByName(type_name) 

384 except KeyError as e: 

385 raise TypeError( 

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

387 ) from e 

388 message_class = message_factory.GetMessageClass(message_descriptor) 

389 return message_class() 

390 

391 

392def Parse(text, 

393 message, 

394 ignore_unknown_fields=False, 

395 descriptor_pool=None, 

396 max_recursion_depth=100): 

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

398 

399 Args: 

400 text: Message JSON representation. 

401 message: A protocol buffer message to merge into. 

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

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

404 default. 

405 max_recursion_depth: max recursion depth of JSON message to be 

406 deserialized. JSON messages over this depth will fail to be 

407 deserialized. Default value is 100. 

408 

409 Returns: 

410 The same message passed as argument. 

411 

412 Raises:: 

413 ParseError: On JSON parsing problems. 

414 """ 

415 if not isinstance(text, str): 

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

417 try: 

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

419 except ValueError as e: 

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

421 return ParseDict(js, message, ignore_unknown_fields, descriptor_pool, 

422 max_recursion_depth) 

423 

424 

425def ParseDict(js_dict, 

426 message, 

427 ignore_unknown_fields=False, 

428 descriptor_pool=None, 

429 max_recursion_depth=100): 

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

431 

432 Args: 

433 js_dict: Dict representation of a JSON message. 

434 message: A protocol buffer message to merge into. 

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

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

437 default. 

438 max_recursion_depth: max recursion depth of JSON message to be 

439 deserialized. JSON messages over this depth will fail to be 

440 deserialized. Default value is 100. 

441 

442 Returns: 

443 The same message passed as argument. 

444 """ 

445 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) 

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

447 return message 

448 

449 

450_INT_OR_FLOAT = (int, float) 

451 

452 

453class _Parser(object): 

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

455 

456 def __init__(self, ignore_unknown_fields, descriptor_pool, 

457 max_recursion_depth): 

458 self.ignore_unknown_fields = ignore_unknown_fields 

459 self.descriptor_pool = descriptor_pool 

460 self.max_recursion_depth = max_recursion_depth 

461 self.recursion_depth = 0 

462 

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

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

465 

466 Args: 

467 value: A JSON object. 

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

469 path: parent path to log parse error info. 

470 

471 Raises: 

472 ParseError: In case of convert problems. 

473 """ 

474 self.recursion_depth += 1 

475 if self.recursion_depth > self.max_recursion_depth: 

476 raise ParseError('Message too deep. Max recursion depth is {0}'.format( 

477 self.max_recursion_depth)) 

478 message_descriptor = message.DESCRIPTOR 

479 full_name = message_descriptor.full_name 

480 if not path: 

481 path = message_descriptor.name 

482 if _IsWrapperMessage(message_descriptor): 

483 self._ConvertWrapperMessage(value, message, path) 

484 elif full_name in _WKTJSONMETHODS: 

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

486 else: 

487 self._ConvertFieldValuePair(value, message, path) 

488 self.recursion_depth -= 1 

489 

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

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

492 

493 Args: 

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

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

496 path: parent path to log parse error info. 

497 

498 Raises: 

499 ParseError: In case of problems converting. 

500 """ 

501 names = [] 

502 message_descriptor = message.DESCRIPTOR 

503 fields_by_json_name = dict((f.json_name, f) 

504 for f in message_descriptor.fields) 

505 for name in js: 

506 try: 

507 field = fields_by_json_name.get(name, None) 

508 if not field: 

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

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

511 if not message_descriptor.is_extendable: 

512 raise ParseError( 

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

514 message_descriptor.full_name, path)) 

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

516 # pylint: disable=protected-access 

517 field = message.Extensions._FindExtensionByName(identifier) 

518 # pylint: enable=protected-access 

519 if not field: 

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

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

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

523 # pylint: disable=protected-access 

524 field = message.Extensions._FindExtensionByName(identifier) 

525 # pylint: enable=protected-access 

526 if not field: 

527 if self.ignore_unknown_fields: 

528 continue 

529 raise ParseError( 

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

531 ' Available Fields(except extensions): "{3}"').format( 

532 message_descriptor.full_name, name, path, 

533 [f.json_name for f in message_descriptor.fields])) 

534 if name in names: 

535 raise ParseError('Message type "{0}" should not have multiple ' 

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

537 message.DESCRIPTOR.full_name, name, path)) 

538 names.append(name) 

539 value = js[name] 

540 # Check no other oneof field is parsed. 

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

542 oneof_name = field.containing_oneof.name 

543 if oneof_name in names: 

544 raise ParseError('Message type "{0}" should not have multiple ' 

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

546 message.DESCRIPTOR.full_name, oneof_name, 

547 path)) 

548 names.append(oneof_name) 

549 

550 if value is None: 

551 if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 

552 and field.message_type.full_name == 'google.protobuf.Value'): 

553 sub_message = getattr(message, field.name) 

554 sub_message.null_value = 0 

555 elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 

556 and field.enum_type.full_name == 'google.protobuf.NullValue'): 

557 setattr(message, field.name, 0) 

558 else: 

559 message.ClearField(field.name) 

560 continue 

561 

562 # Parse field value. 

563 if _IsMapEntry(field): 

564 message.ClearField(field.name) 

565 self._ConvertMapFieldValue(value, message, field, 

566 '{0}.{1}'.format(path, name)) 

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

568 message.ClearField(field.name) 

569 if not isinstance(value, list): 

570 raise ParseError('repeated field {0} must be in [] which is ' 

571 '{1} at {2}'.format(name, value, path)) 

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

573 # Repeated message field. 

574 for index, item in enumerate(value): 

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

576 # None is a null_value in Value. 

577 if (item is None and 

578 sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): 

579 raise ParseError('null is not allowed to be used as an element' 

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

581 path, name, index)) 

582 self.ConvertMessage(item, sub_message, 

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

584 else: 

585 # Repeated scalar field. 

586 for index, item in enumerate(value): 

587 if item is None: 

588 raise ParseError('null is not allowed to be used as an element' 

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

590 path, name, index)) 

591 getattr(message, field.name).append( 

592 _ConvertScalarFieldValue( 

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

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

595 if field.is_extension: 

596 sub_message = message.Extensions[field] 

597 else: 

598 sub_message = getattr(message, field.name) 

599 sub_message.SetInParent() 

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

601 else: 

602 if field.is_extension: 

603 message.Extensions[field] = _ConvertScalarFieldValue( 

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

605 else: 

606 setattr( 

607 message, field.name, 

608 _ConvertScalarFieldValue(value, field, 

609 '{0}.{1}'.format(path, name))) 

610 except ParseError as e: 

611 if field and field.containing_oneof is None: 

612 raise ParseError( 

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

614 ) from e 

615 else: 

616 raise ParseError(str(e)) from e 

617 except ValueError as e: 

618 raise ParseError( 

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

620 ) from e 

621 except TypeError as e: 

622 raise ParseError( 

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

624 ) from e 

625 

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

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

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

629 return 

630 try: 

631 type_url = value['@type'] 

632 except KeyError as e: 

633 raise ParseError( 

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

635 ) from e 

636 

637 try: 

638 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 

639 except TypeError as e: 

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

641 message_descriptor = sub_message.DESCRIPTOR 

642 full_name = message_descriptor.full_name 

643 if _IsWrapperMessage(message_descriptor): 

644 self._ConvertWrapperMessage(value['value'], sub_message, 

645 '{0}.value'.format(path)) 

646 elif full_name in _WKTJSONMETHODS: 

647 methodcaller(_WKTJSONMETHODS[full_name][1], value['value'], sub_message, 

648 '{0}.value'.format(path))( 

649 self) 

650 else: 

651 del value['@type'] 

652 self._ConvertFieldValuePair(value, sub_message, path) 

653 value['@type'] = type_url 

654 # Sets Any message 

655 message.value = sub_message.SerializeToString() 

656 message.type_url = type_url 

657 

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

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

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

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

662 try: 

663 message.FromJsonString(value) 

664 except ValueError as e: 

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

666 

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

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

669 if isinstance(value, dict): 

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

671 elif isinstance(value, list): 

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

673 elif value is None: 

674 message.null_value = 0 

675 elif isinstance(value, bool): 

676 message.bool_value = value 

677 elif isinstance(value, str): 

678 message.string_value = value 

679 elif isinstance(value, _INT_OR_FLOAT): 

680 message.number_value = value 

681 else: 

682 raise ParseError('Value {0} has unexpected type {1} at {2}'.format( 

683 value, type(value), path)) 

684 

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

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

687 if not isinstance(value, list): 

688 raise ParseError('ListValue must be in [] which is {0} at {1}'.format( 

689 value, path)) 

690 message.ClearField('values') 

691 for index, item in enumerate(value): 

692 self._ConvertValueMessage(item, message.values.add(), 

693 '{0}[{1}]'.format(path, index)) 

694 

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

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

697 if not isinstance(value, dict): 

698 raise ParseError('Struct must be in a dict which is {0} at {1}'.format( 

699 value, path)) 

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

701 # there are no values. 

702 message.Clear() 

703 for key in value: 

704 self._ConvertValueMessage(value[key], message.fields[key], 

705 '{0}.{1}'.format(path, key)) 

706 return 

707 

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

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

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

711 setattr( 

712 message, 'value', 

713 _ConvertScalarFieldValue(value, field, path='{0}.value'.format(path))) 

714 

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

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

717 

718 Args: 

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

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

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

722 path: parent path to log parse error info. 

723 

724 Raises: 

725 ParseError: In case of convert problems. 

726 """ 

727 if not isinstance(value, dict): 

728 raise ParseError( 

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

730 field.name, value, path)) 

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

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

733 for key in value: 

734 key_value = _ConvertScalarFieldValue(key, key_field, 

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

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

737 self.ConvertMessage(value[key], 

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

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

740 else: 

741 getattr(message, field.name)[key_value] = _ConvertScalarFieldValue( 

742 value[key], value_field, path='{0}[{1}]'.format(path, key_value)) 

743 

744 

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

746 """Convert a single scalar field value. 

747 

748 Args: 

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

750 field: The descriptor of the field to convert. 

751 path: parent path to log parse error info. 

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

753 

754 Returns: 

755 The converted scalar field value 

756 

757 Raises: 

758 ParseError: In case of convert problems. 

759 """ 

760 try: 

761 if field.cpp_type in _INT_TYPES: 

762 return _ConvertInteger(value) 

763 elif field.cpp_type in _FLOAT_TYPES: 

764 return _ConvertFloat(value, field) 

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

766 return _ConvertBool(value, require_str) 

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

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

769 if isinstance(value, str): 

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

771 else: 

772 encoded = value 

773 # Add extra padding '=' 

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

775 return base64.urlsafe_b64decode(padded_value) 

776 else: 

777 # Checking for unpaired surrogates appears to be unreliable, 

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

779 if _UNPAIRED_SURROGATE_PATTERN.search(value): 

780 raise ParseError('Unpaired surrogate') 

781 return value 

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

783 # Convert an enum value. 

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

785 if enum_value is None: 

786 try: 

787 number = int(value) 

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

789 except ValueError as e: 

790 raise ParseError('Invalid enum value {0} for enum type {1}'.format( 

791 value, field.enum_type.full_name)) from e 

792 if enum_value is None: 

793 if field.enum_type.is_closed: 

794 raise ParseError('Invalid enum value {0} for enum type {1}'.format( 

795 value, field.enum_type.full_name)) 

796 else: 

797 return number 

798 return enum_value.number 

799 except ParseError as e: 

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

801 

802 

803def _ConvertInteger(value): 

804 """Convert an integer. 

805 

806 Args: 

807 value: A scalar value to convert. 

808 

809 Returns: 

810 The integer value. 

811 

812 Raises: 

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

814 """ 

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

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

817 

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

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

820 

821 if isinstance(value, bool): 

822 raise ParseError('Bool value {0} is not acceptable for ' 

823 'integer field'.format(value)) 

824 

825 return int(value) 

826 

827 

828def _ConvertFloat(value, field): 

829 """Convert an floating point number.""" 

830 if isinstance(value, float): 

831 if math.isnan(value): 

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

833 if math.isinf(value): 

834 if value > 0: 

835 raise ParseError('Couldn\'t parse Infinity or value too large, ' 

836 'use quoted "Infinity" instead') 

837 else: 

838 raise ParseError('Couldn\'t parse -Infinity or value too small, ' 

839 'use quoted "-Infinity" instead') 

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

841 # pylint: disable=protected-access 

842 if value > type_checkers._FLOAT_MAX: 

843 raise ParseError('Float value too large') 

844 # pylint: disable=protected-access 

845 if value < type_checkers._FLOAT_MIN: 

846 raise ParseError('Float value too small') 

847 if value == 'nan': 

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

849 try: 

850 # Assume Python compatible syntax. 

851 return float(value) 

852 except ValueError as e: 

853 # Check alternative spellings. 

854 if value == _NEG_INFINITY: 

855 return float('-inf') 

856 elif value == _INFINITY: 

857 return float('inf') 

858 elif value == _NAN: 

859 return float('nan') 

860 else: 

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

862 

863 

864def _ConvertBool(value, require_str): 

865 """Convert a boolean value. 

866 

867 Args: 

868 value: A scalar value to convert. 

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

870 

871 Returns: 

872 The bool parsed. 

873 

874 Raises: 

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

876 """ 

877 if require_str: 

878 if value == 'true': 

879 return True 

880 elif value == 'false': 

881 return False 

882 else: 

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

884 

885 if not isinstance(value, bool): 

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

887 return value 

888 

889_WKTJSONMETHODS = { 

890 'google.protobuf.Any': ['_AnyMessageToJsonObject', 

891 '_ConvertAnyMessage'], 

892 'google.protobuf.Duration': ['_GenericMessageToJsonObject', 

893 '_ConvertGenericMessage'], 

894 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject', 

895 '_ConvertGenericMessage'], 

896 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject', 

897 '_ConvertListValueMessage'], 

898 'google.protobuf.Struct': ['_StructMessageToJsonObject', 

899 '_ConvertStructMessage'], 

900 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject', 

901 '_ConvertGenericMessage'], 

902 'google.protobuf.Value': ['_ValueMessageToJsonObject', 

903 '_ConvertValueMessage'] 

904}