1# Protocol Buffers - Google's data interchange format 2# Copyright 2008 Google Inc. All rights reserved. 3# https://developers.google.com/protocol-buffers/ 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are 7# met: 8# 9# * Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# * Redistributions in binary form must reproduce the above 12# copyright notice, this list of conditions and the following disclaimer 13# in the documentation and/or other materials provided with the 14# distribution. 15# * Neither the name of Google Inc. nor the names of its 16# contributors may be used to endorse or promote products derived from 17# this software without specific prior written permission. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31"""Contains routines for printing protocol messages in JSON format. 32 33Simple usage example: 34 35 # Create a proto object and serialize it to a json format string. 36 message = my_proto_pb2.MyMessage(foo='bar') 37 json_string = json_format.MessageToJson(message) 38 39 # Parse a json format string to proto object. 40 message = json_format.Parse(json_string, my_proto_pb2.MyMessage()) 41""" 42 43__author__ = 'jieluo@google.com (Jie Luo)' 44 45# pylint: disable=g-statement-before-imports,g-import-not-at-top 46try: 47 from collections import OrderedDict 48except ImportError: 49 from ordereddict import OrderedDict # PY26 50# pylint: enable=g-statement-before-imports,g-import-not-at-top 51 52import base64 53import json 54import math 55 56from operator import methodcaller 57 58import re 59import sys 60 61import six 62 63from google.protobuf.internal import type_checkers 64from google.protobuf import descriptor 65from google.protobuf import symbol_database 66 67 68_TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S' 69_INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32, 70 descriptor.FieldDescriptor.CPPTYPE_UINT32, 71 descriptor.FieldDescriptor.CPPTYPE_INT64, 72 descriptor.FieldDescriptor.CPPTYPE_UINT64]) 73_INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64, 74 descriptor.FieldDescriptor.CPPTYPE_UINT64]) 75_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT, 76 descriptor.FieldDescriptor.CPPTYPE_DOUBLE]) 77_INFINITY = 'Infinity' 78_NEG_INFINITY = '-Infinity' 79_NAN = 'NaN' 80 81_UNPAIRED_SURROGATE_PATTERN = re.compile(six.u( 82 r'[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]' 83)) 84 85_VALID_EXTENSION_NAME = re.compile(r'\[[a-zA-Z0-9\._]*\]$') 86 87 88class Error(Exception): 89 """Top-level module error for json_format.""" 90 91 92class SerializeToJsonError(Error): 93 """Thrown if serialization to JSON fails.""" 94 95 96class ParseError(Error): 97 """Thrown in case of parsing error.""" 98 99 100def MessageToJson( 101 message, 102 including_default_value_fields=False, 103 preserving_proto_field_name=False, 104 indent=2, 105 sort_keys=False, 106 use_integers_for_enums=False, 107 descriptor_pool=None, 108 float_precision=None): 109 """Converts protobuf message to JSON format. 110 111 Args: 112 message: The protocol buffers message instance to serialize. 113 including_default_value_fields: If True, singular primitive fields, 114 repeated fields, and map fields will always be serialized. If 115 False, only serialize non-empty fields. Singular message fields 116 and oneof fields are not affected by this option. 117 preserving_proto_field_name: If True, use the original proto field 118 names as defined in the .proto file. If False, convert the field 119 names to lowerCamelCase. 120 indent: The JSON object will be pretty-printed with this indent level. 121 An indent level of 0 or negative will only insert newlines. 122 sort_keys: If True, then the output will be sorted by field names. 123 use_integers_for_enums: If true, print integers instead of enum names. 124 descriptor_pool: A Descriptor Pool for resolving types. If None use the 125 default. 126 float_precision: If set, use this to specify float field valid digits. 127 128 Returns: 129 A string containing the JSON formatted protocol buffer message. 130 """ 131 printer = _Printer( 132 including_default_value_fields, 133 preserving_proto_field_name, 134 use_integers_for_enums, 135 descriptor_pool, 136 float_precision=float_precision) 137 return printer.ToJsonString(message, indent, sort_keys) 138 139 140def MessageToDict( 141 message, 142 including_default_value_fields=False, 143 preserving_proto_field_name=False, 144 use_integers_for_enums=False, 145 descriptor_pool=None, 146 float_precision=None): 147 """Converts protobuf message to a dictionary. 148 149 When the dictionary is encoded to JSON, it conforms to proto3 JSON spec. 150 151 Args: 152 message: The protocol buffers message instance to serialize. 153 including_default_value_fields: If True, singular primitive fields, 154 repeated fields, and map fields will always be serialized. If 155 False, only serialize non-empty fields. Singular message fields 156 and oneof fields are not affected by this option. 157 preserving_proto_field_name: If True, use the original proto field 158 names as defined in the .proto file. If False, convert the field 159 names to lowerCamelCase. 160 use_integers_for_enums: If true, print integers instead of enum names. 161 descriptor_pool: A Descriptor Pool for resolving types. If None use the 162 default. 163 float_precision: If set, use this to specify float field valid digits. 164 165 Returns: 166 A dict representation of the protocol buffer message. 167 """ 168 printer = _Printer( 169 including_default_value_fields, 170 preserving_proto_field_name, 171 use_integers_for_enums, 172 descriptor_pool, 173 float_precision=float_precision) 174 # pylint: disable=protected-access 175 return printer._MessageToJsonObject(message) 176 177 178def _IsMapEntry(field): 179 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and 180 field.message_type.has_options and 181 field.message_type.GetOptions().map_entry) 182 183 184class _Printer(object): 185 """JSON format printer for protocol message.""" 186 187 def __init__( 188 self, 189 including_default_value_fields=False, 190 preserving_proto_field_name=False, 191 use_integers_for_enums=False, 192 descriptor_pool=None, 193 float_precision=None): 194 self.including_default_value_fields = including_default_value_fields 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 if float_precision: 199 self.float_format = '.{}g'.format(float_precision) 200 else: 201 self.float_format = None 202 203 def ToJsonString(self, message, indent, sort_keys): 204 js = self._MessageToJsonObject(message) 205 return json.dumps(js, indent=indent, sort_keys=sort_keys) 206 207 def _MessageToJsonObject(self, message): 208 """Converts message to an object according to Proto3 JSON Specification.""" 209 message_descriptor = message.DESCRIPTOR 210 full_name = message_descriptor.full_name 211 if _IsWrapperMessage(message_descriptor): 212 return self._WrapperMessageToJsonObject(message) 213 if full_name in _WKTJSONMETHODS: 214 return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self) 215 js = {} 216 return self._RegularMessageToJsonObject(message, js) 217 218 def _RegularMessageToJsonObject(self, message, js): 219 """Converts normal message according to Proto3 JSON Specification.""" 220 fields = message.ListFields() 221 222 try: 223 for field, value in fields: 224 if self.preserving_proto_field_name: 225 name = field.name 226 else: 227 name = field.json_name 228 if _IsMapEntry(field): 229 # Convert a map field. 230 v_field = field.message_type.fields_by_name['value'] 231 js_map = {} 232 for key in value: 233 if isinstance(key, bool): 234 if key: 235 recorded_key = 'true' 236 else: 237 recorded_key = 'false' 238 else: 239 recorded_key = key 240 js_map[recorded_key] = self._FieldToJsonObject( 241 v_field, value[key]) 242 js[name] = js_map 243 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 244 # Convert a repeated field. 245 js[name] = [self._FieldToJsonObject(field, k) 246 for k in value] 247 elif field.is_extension: 248 name = '[%s]' % field.full_name 249 js[name] = self._FieldToJsonObject(field, value) 250 else: 251 js[name] = self._FieldToJsonObject(field, value) 252 253 # Serialize default value if including_default_value_fields is True. 254 if self.including_default_value_fields: 255 message_descriptor = message.DESCRIPTOR 256 for field in message_descriptor.fields: 257 # Singular message fields and oneof fields will not be affected. 258 if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and 259 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or 260 field.containing_oneof): 261 continue 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.label == descriptor.FieldDescriptor.LABEL_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 280 return js 281 282 def _FieldToJsonObject(self, field, value): 283 """Converts field value according to Proto3 JSON Specification.""" 284 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 285 return self._MessageToJsonObject(value) 286 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 287 if self.use_integers_for_enums: 288 return value 289 enum_value = field.enum_type.values_by_number.get(value, None) 290 if enum_value is not None: 291 return enum_value.name 292 else: 293 if field.file.syntax == 'proto3': 294 return value 295 raise SerializeToJsonError('Enum field contains an integer value ' 296 'which can not mapped to an enum value.') 297 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 298 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: 299 # Use base64 Data encoding for bytes 300 return base64.b64encode(value).decode('utf-8') 301 else: 302 return value 303 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 304 return bool(value) 305 elif field.cpp_type in _INT64_TYPES: 306 return str(value) 307 elif field.cpp_type in _FLOAT_TYPES: 308 if math.isinf(value): 309 if value < 0.0: 310 return _NEG_INFINITY 311 else: 312 return _INFINITY 313 if math.isnan(value): 314 return _NAN 315 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: 316 if self.float_format: 317 return float(format(value, self.float_format)) 318 else: 319 return type_checkers.ToShortestFloat(value) 320 321 return value 322 323 def _AnyMessageToJsonObject(self, message): 324 """Converts Any message according to Proto3 JSON Specification.""" 325 if not message.ListFields(): 326 return {} 327 # Must print @type first, use OrderedDict instead of {} 328 js = OrderedDict() 329 type_url = message.type_url 330 js['@type'] = type_url 331 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 332 sub_message.ParseFromString(message.value) 333 message_descriptor = sub_message.DESCRIPTOR 334 full_name = message_descriptor.full_name 335 if _IsWrapperMessage(message_descriptor): 336 js['value'] = self._WrapperMessageToJsonObject(sub_message) 337 return js 338 if full_name in _WKTJSONMETHODS: 339 js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0], 340 sub_message)(self) 341 return js 342 return self._RegularMessageToJsonObject(sub_message, js) 343 344 def _GenericMessageToJsonObject(self, message): 345 """Converts message according to Proto3 JSON Specification.""" 346 # Duration, Timestamp and FieldMask have ToJsonString method to do the 347 # convert. Users can also call the method directly. 348 return message.ToJsonString() 349 350 def _ValueMessageToJsonObject(self, message): 351 """Converts Value message according to Proto3 JSON Specification.""" 352 which = message.WhichOneof('kind') 353 # If the Value message is not set treat as null_value when serialize 354 # to JSON. The parse back result will be different from original message. 355 if which is None or which == 'null_value': 356 return None 357 if which == 'list_value': 358 return self._ListValueMessageToJsonObject(message.list_value) 359 if which == 'struct_value': 360 value = message.struct_value 361 else: 362 value = getattr(message, which) 363 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 364 return self._FieldToJsonObject(oneof_descriptor, value) 365 366 def _ListValueMessageToJsonObject(self, message): 367 """Converts ListValue message according to Proto3 JSON Specification.""" 368 return [self._ValueMessageToJsonObject(value) 369 for value in message.values] 370 371 def _StructMessageToJsonObject(self, message): 372 """Converts Struct message according to Proto3 JSON Specification.""" 373 fields = message.fields 374 ret = {} 375 for key in fields: 376 ret[key] = self._ValueMessageToJsonObject(fields[key]) 377 return ret 378 379 def _WrapperMessageToJsonObject(self, message): 380 return self._FieldToJsonObject( 381 message.DESCRIPTOR.fields_by_name['value'], message.value) 382 383 384def _IsWrapperMessage(message_descriptor): 385 return message_descriptor.file.name == 'google/protobuf/wrappers.proto' 386 387 388def _DuplicateChecker(js): 389 result = {} 390 for name, value in js: 391 if name in result: 392 raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name)) 393 result[name] = value 394 return result 395 396 397def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 398 """Creates a message from a type URL.""" 399 db = symbol_database.Default() 400 pool = db.pool if descriptor_pool is None else descriptor_pool 401 type_name = type_url.split('/')[-1] 402 try: 403 message_descriptor = pool.FindMessageTypeByName(type_name) 404 except KeyError: 405 raise TypeError( 406 'Can not find message descriptor by type_url: {0}.'.format(type_url)) 407 message_class = db.GetPrototype(message_descriptor) 408 return message_class() 409 410 411def Parse(text, message, ignore_unknown_fields=False, descriptor_pool=None): 412 """Parses a JSON representation of a protocol message into a message. 413 414 Args: 415 text: Message JSON representation. 416 message: A protocol buffer message to merge into. 417 ignore_unknown_fields: If True, do not raise errors for unknown fields. 418 descriptor_pool: A Descriptor Pool for resolving types. If None use the 419 default. 420 421 Returns: 422 The same message passed as argument. 423 424 Raises:: 425 ParseError: On JSON parsing problems. 426 """ 427 if not isinstance(text, six.text_type): text = text.decode('utf-8') 428 try: 429 js = json.loads(text, object_pairs_hook=_DuplicateChecker) 430 except ValueError as e: 431 raise ParseError('Failed to load JSON: {0}.'.format(str(e))) 432 return ParseDict(js, message, ignore_unknown_fields, descriptor_pool) 433 434 435def ParseDict(js_dict, 436 message, 437 ignore_unknown_fields=False, 438 descriptor_pool=None): 439 """Parses a JSON dictionary representation into a message. 440 441 Args: 442 js_dict: Dict representation of a JSON message. 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 448 Returns: 449 The same message passed as argument. 450 """ 451 parser = _Parser(ignore_unknown_fields, descriptor_pool) 452 parser.ConvertMessage(js_dict, message) 453 return message 454 455 456_INT_OR_FLOAT = six.integer_types + (float,) 457 458 459class _Parser(object): 460 """JSON format parser for protocol message.""" 461 462 def __init__(self, ignore_unknown_fields, descriptor_pool): 463 self.ignore_unknown_fields = ignore_unknown_fields 464 self.descriptor_pool = descriptor_pool 465 466 def ConvertMessage(self, value, message): 467 """Convert a JSON object into a message. 468 469 Args: 470 value: A JSON object. 471 message: A WKT or regular protocol message to record the data. 472 473 Raises: 474 ParseError: In case of convert problems. 475 """ 476 message_descriptor = message.DESCRIPTOR 477 full_name = message_descriptor.full_name 478 if _IsWrapperMessage(message_descriptor): 479 self._ConvertWrapperMessage(value, message) 480 elif full_name in _WKTJSONMETHODS: 481 methodcaller(_WKTJSONMETHODS[full_name][1], value, message)(self) 482 else: 483 self._ConvertFieldValuePair(value, message) 484 485 def _ConvertFieldValuePair(self, js, message): 486 """Convert field value pairs into regular message. 487 488 Args: 489 js: A JSON object to convert the field value pairs. 490 message: A regular protocol message to record the data. 491 492 Raises: 493 ParseError: In case of problems converting. 494 """ 495 names = [] 496 message_descriptor = message.DESCRIPTOR 497 fields_by_json_name = dict((f.json_name, f) 498 for f in message_descriptor.fields) 499 for name in js: 500 try: 501 field = fields_by_json_name.get(name, None) 502 if not field: 503 field = message_descriptor.fields_by_name.get(name, None) 504 if not field and _VALID_EXTENSION_NAME.match(name): 505 if not message_descriptor.is_extendable: 506 raise ParseError('Message type {0} does not have extensions'.format( 507 message_descriptor.full_name)) 508 identifier = name[1:-1] # strip [] brackets 509 # pylint: disable=protected-access 510 field = message.Extensions._FindExtensionByName(identifier) 511 # pylint: enable=protected-access 512 if not field: 513 # Try looking for extension by the message type name, dropping the 514 # field name following the final . separator in full_name. 515 identifier = '.'.join(identifier.split('.')[:-1]) 516 # pylint: disable=protected-access 517 field = message.Extensions._FindExtensionByName(identifier) 518 # pylint: enable=protected-access 519 if not field: 520 if self.ignore_unknown_fields: 521 continue 522 raise ParseError( 523 ('Message type "{0}" has no field named "{1}".\n' 524 ' Available Fields(except extensions): {2}').format( 525 message_descriptor.full_name, name, 526 [f.json_name for f in message_descriptor.fields])) 527 if name in names: 528 raise ParseError('Message type "{0}" should not have multiple ' 529 '"{1}" fields.'.format( 530 message.DESCRIPTOR.full_name, name)) 531 names.append(name) 532 # Check no other oneof field is parsed. 533 if field.containing_oneof is not None: 534 oneof_name = field.containing_oneof.name 535 if oneof_name in names: 536 raise ParseError('Message type "{0}" should not have multiple ' 537 '"{1}" oneof fields.'.format( 538 message.DESCRIPTOR.full_name, oneof_name)) 539 names.append(oneof_name) 540 541 value = js[name] 542 if value is None: 543 if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 544 and field.message_type.full_name == 'google.protobuf.Value'): 545 sub_message = getattr(message, field.name) 546 sub_message.null_value = 0 547 else: 548 message.ClearField(field.name) 549 continue 550 551 # Parse field value. 552 if _IsMapEntry(field): 553 message.ClearField(field.name) 554 self._ConvertMapFieldValue(value, message, field) 555 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 556 message.ClearField(field.name) 557 if not isinstance(value, list): 558 raise ParseError('repeated field {0} must be in [] which is ' 559 '{1}.'.format(name, value)) 560 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 561 # Repeated message field. 562 for item in value: 563 sub_message = getattr(message, field.name).add() 564 # None is a null_value in Value. 565 if (item is None and 566 sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): 567 raise ParseError('null is not allowed to be used as an element' 568 ' in a repeated field.') 569 self.ConvertMessage(item, sub_message) 570 else: 571 # Repeated scalar field. 572 for item in value: 573 if item is None: 574 raise ParseError('null is not allowed to be used as an element' 575 ' in a repeated field.') 576 getattr(message, field.name).append( 577 _ConvertScalarFieldValue(item, field)) 578 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 579 if field.is_extension: 580 sub_message = message.Extensions[field] 581 else: 582 sub_message = getattr(message, field.name) 583 sub_message.SetInParent() 584 self.ConvertMessage(value, sub_message) 585 else: 586 if field.is_extension: 587 message.Extensions[field] = _ConvertScalarFieldValue(value, field) 588 else: 589 setattr(message, field.name, _ConvertScalarFieldValue(value, field)) 590 except ParseError as e: 591 if field and field.containing_oneof is None: 592 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) 593 else: 594 raise ParseError(str(e)) 595 except ValueError as e: 596 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) 597 except TypeError as e: 598 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) 599 600 def _ConvertAnyMessage(self, value, message): 601 """Convert a JSON representation into Any message.""" 602 if isinstance(value, dict) and not value: 603 return 604 try: 605 type_url = value['@type'] 606 except KeyError: 607 raise ParseError('@type is missing when parsing any message.') 608 609 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 610 message_descriptor = sub_message.DESCRIPTOR 611 full_name = message_descriptor.full_name 612 if _IsWrapperMessage(message_descriptor): 613 self._ConvertWrapperMessage(value['value'], sub_message) 614 elif full_name in _WKTJSONMETHODS: 615 methodcaller( 616 _WKTJSONMETHODS[full_name][1], value['value'], sub_message)(self) 617 else: 618 del value['@type'] 619 self._ConvertFieldValuePair(value, sub_message) 620 value['@type'] = type_url 621 # Sets Any message 622 message.value = sub_message.SerializeToString() 623 message.type_url = type_url 624 625 def _ConvertGenericMessage(self, value, message): 626 """Convert a JSON representation into message with FromJsonString.""" 627 # Duration, Timestamp, FieldMask have a FromJsonString method to do the 628 # conversion. Users can also call the method directly. 629 try: 630 message.FromJsonString(value) 631 except ValueError as e: 632 raise ParseError(e) 633 634 def _ConvertValueMessage(self, value, message): 635 """Convert a JSON representation into Value message.""" 636 if isinstance(value, dict): 637 self._ConvertStructMessage(value, message.struct_value) 638 elif isinstance(value, list): 639 self. _ConvertListValueMessage(value, message.list_value) 640 elif value is None: 641 message.null_value = 0 642 elif isinstance(value, bool): 643 message.bool_value = value 644 elif isinstance(value, six.string_types): 645 message.string_value = value 646 elif isinstance(value, _INT_OR_FLOAT): 647 message.number_value = value 648 else: 649 raise ParseError('Value {0} has unexpected type {1}.'.format( 650 value, type(value))) 651 652 def _ConvertListValueMessage(self, value, message): 653 """Convert a JSON representation into ListValue message.""" 654 if not isinstance(value, list): 655 raise ParseError( 656 'ListValue must be in [] which is {0}.'.format(value)) 657 message.ClearField('values') 658 for item in value: 659 self._ConvertValueMessage(item, message.values.add()) 660 661 def _ConvertStructMessage(self, value, message): 662 """Convert a JSON representation into Struct message.""" 663 if not isinstance(value, dict): 664 raise ParseError( 665 'Struct must be in a dict which is {0}.'.format(value)) 666 # Clear will mark the struct as modified so it will be created even if 667 # there are no values. 668 message.Clear() 669 for key in value: 670 self._ConvertValueMessage(value[key], message.fields[key]) 671 return 672 673 def _ConvertWrapperMessage(self, value, message): 674 """Convert a JSON representation into Wrapper message.""" 675 field = message.DESCRIPTOR.fields_by_name['value'] 676 setattr(message, 'value', _ConvertScalarFieldValue(value, field)) 677 678 def _ConvertMapFieldValue(self, value, message, field): 679 """Convert map field value for a message map field. 680 681 Args: 682 value: A JSON object to convert the map field value. 683 message: A protocol message to record the converted data. 684 field: The descriptor of the map field to be converted. 685 686 Raises: 687 ParseError: In case of convert problems. 688 """ 689 if not isinstance(value, dict): 690 raise ParseError( 691 'Map field {0} must be in a dict which is {1}.'.format( 692 field.name, value)) 693 key_field = field.message_type.fields_by_name['key'] 694 value_field = field.message_type.fields_by_name['value'] 695 for key in value: 696 key_value = _ConvertScalarFieldValue(key, key_field, True) 697 if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 698 self.ConvertMessage(value[key], getattr( 699 message, field.name)[key_value]) 700 else: 701 getattr(message, field.name)[key_value] = _ConvertScalarFieldValue( 702 value[key], value_field) 703 704 705def _ConvertScalarFieldValue(value, field, require_str=False): 706 """Convert a single scalar field value. 707 708 Args: 709 value: A scalar value to convert the scalar field value. 710 field: The descriptor of the field to convert. 711 require_str: If True, the field value must be a str. 712 713 Returns: 714 The converted scalar field value 715 716 Raises: 717 ParseError: In case of convert problems. 718 """ 719 if field.cpp_type in _INT_TYPES: 720 return _ConvertInteger(value) 721 elif field.cpp_type in _FLOAT_TYPES: 722 return _ConvertFloat(value, field) 723 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 724 return _ConvertBool(value, require_str) 725 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 726 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: 727 if isinstance(value, six.text_type): 728 encoded = value.encode('utf-8') 729 else: 730 encoded = value 731 # Add extra padding '=' 732 padded_value = encoded + b'=' * (4 - len(encoded) % 4) 733 return base64.urlsafe_b64decode(padded_value) 734 else: 735 # Checking for unpaired surrogates appears to be unreliable, 736 # depending on the specific Python version, so we check manually. 737 if _UNPAIRED_SURROGATE_PATTERN.search(value): 738 raise ParseError('Unpaired surrogate') 739 return value 740 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 741 # Convert an enum value. 742 enum_value = field.enum_type.values_by_name.get(value, None) 743 if enum_value is None: 744 try: 745 number = int(value) 746 enum_value = field.enum_type.values_by_number.get(number, None) 747 except ValueError: 748 raise ParseError('Invalid enum value {0} for enum type {1}.'.format( 749 value, field.enum_type.full_name)) 750 if enum_value is None: 751 if field.file.syntax == 'proto3': 752 # Proto3 accepts unknown enums. 753 return number 754 raise ParseError('Invalid enum value {0} for enum type {1}.'.format( 755 value, field.enum_type.full_name)) 756 return enum_value.number 757 758 759def _ConvertInteger(value): 760 """Convert an integer. 761 762 Args: 763 value: A scalar value to convert. 764 765 Returns: 766 The integer value. 767 768 Raises: 769 ParseError: If an integer couldn't be consumed. 770 """ 771 if isinstance(value, float) and not value.is_integer(): 772 raise ParseError('Couldn\'t parse integer: {0}.'.format(value)) 773 774 if isinstance(value, six.text_type) and value.find(' ') != -1: 775 raise ParseError('Couldn\'t parse integer: "{0}".'.format(value)) 776 777 if isinstance(value, bool): 778 raise ParseError('Bool value {0} is not acceptable for ' 779 'integer field.'.format(value)) 780 781 return int(value) 782 783 784def _ConvertFloat(value, field): 785 """Convert an floating point number.""" 786 if isinstance(value, float): 787 if math.isnan(value): 788 raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead.') 789 if math.isinf(value): 790 if value > 0: 791 raise ParseError('Couldn\'t parse Infinity or value too large, ' 792 'use quoted "Infinity" instead.') 793 else: 794 raise ParseError('Couldn\'t parse -Infinity or value too small, ' 795 'use quoted "-Infinity" instead.') 796 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: 797 # pylint: disable=protected-access 798 if value > type_checkers._FLOAT_MAX: 799 raise ParseError('Float value too large') 800 # pylint: disable=protected-access 801 if value < type_checkers._FLOAT_MIN: 802 raise ParseError('Float value too small') 803 if value == 'nan': 804 raise ParseError('Couldn\'t parse float "nan", use "NaN" instead.') 805 try: 806 # Assume Python compatible syntax. 807 return float(value) 808 except ValueError: 809 # Check alternative spellings. 810 if value == _NEG_INFINITY: 811 return float('-inf') 812 elif value == _INFINITY: 813 return float('inf') 814 elif value == _NAN: 815 return float('nan') 816 else: 817 raise ParseError('Couldn\'t parse float: {0}.'.format(value)) 818 819 820def _ConvertBool(value, require_str): 821 """Convert a boolean value. 822 823 Args: 824 value: A scalar value to convert. 825 require_str: If True, value must be a str. 826 827 Returns: 828 The bool parsed. 829 830 Raises: 831 ParseError: If a boolean value couldn't be consumed. 832 """ 833 if require_str: 834 if value == 'true': 835 return True 836 elif value == 'false': 837 return False 838 else: 839 raise ParseError('Expected "true" or "false", not {0}.'.format(value)) 840 841 if not isinstance(value, bool): 842 raise ParseError('Expected true or false without quotes.') 843 return value 844 845_WKTJSONMETHODS = { 846 'google.protobuf.Any': ['_AnyMessageToJsonObject', 847 '_ConvertAnyMessage'], 848 'google.protobuf.Duration': ['_GenericMessageToJsonObject', 849 '_ConvertGenericMessage'], 850 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject', 851 '_ConvertGenericMessage'], 852 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject', 853 '_ConvertListValueMessage'], 854 'google.protobuf.Struct': ['_StructMessageToJsonObject', 855 '_ConvertStructMessage'], 856 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject', 857 '_ConvertGenericMessage'], 858 'google.protobuf.Value': ['_ValueMessageToJsonObject', 859 '_ConvertValueMessage'] 860} 861