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 if field.enum_type.full_name == 'google.protobuf.NullValue': 290 return None 291 enum_value = field.enum_type.values_by_number.get(value, None) 292 if enum_value is not None: 293 return enum_value.name 294 else: 295 if field.file.syntax == 'proto3': 296 return value 297 raise SerializeToJsonError('Enum field contains an integer value ' 298 'which can not mapped to an enum value.') 299 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 300 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: 301 # Use base64 Data encoding for bytes 302 return base64.b64encode(value).decode('utf-8') 303 else: 304 return value 305 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 306 return bool(value) 307 elif field.cpp_type in _INT64_TYPES: 308 return str(value) 309 elif field.cpp_type in _FLOAT_TYPES: 310 if math.isinf(value): 311 if value < 0.0: 312 return _NEG_INFINITY 313 else: 314 return _INFINITY 315 if math.isnan(value): 316 return _NAN 317 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: 318 if self.float_format: 319 return float(format(value, self.float_format)) 320 else: 321 return type_checkers.ToShortestFloat(value) 322 323 return value 324 325 def _AnyMessageToJsonObject(self, message): 326 """Converts Any message according to Proto3 JSON Specification.""" 327 if not message.ListFields(): 328 return {} 329 # Must print @type first, use OrderedDict instead of {} 330 js = OrderedDict() 331 type_url = message.type_url 332 js['@type'] = type_url 333 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 334 sub_message.ParseFromString(message.value) 335 message_descriptor = sub_message.DESCRIPTOR 336 full_name = message_descriptor.full_name 337 if _IsWrapperMessage(message_descriptor): 338 js['value'] = self._WrapperMessageToJsonObject(sub_message) 339 return js 340 if full_name in _WKTJSONMETHODS: 341 js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0], 342 sub_message)(self) 343 return js 344 return self._RegularMessageToJsonObject(sub_message, js) 345 346 def _GenericMessageToJsonObject(self, message): 347 """Converts message according to Proto3 JSON Specification.""" 348 # Duration, Timestamp and FieldMask have ToJsonString method to do the 349 # convert. Users can also call the method directly. 350 return message.ToJsonString() 351 352 def _ValueMessageToJsonObject(self, message): 353 """Converts Value message according to Proto3 JSON Specification.""" 354 which = message.WhichOneof('kind') 355 # If the Value message is not set treat as null_value when serialize 356 # to JSON. The parse back result will be different from original message. 357 if which is None or which == 'null_value': 358 return None 359 if which == 'list_value': 360 return self._ListValueMessageToJsonObject(message.list_value) 361 if which == 'struct_value': 362 value = message.struct_value 363 else: 364 value = getattr(message, which) 365 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] 366 return self._FieldToJsonObject(oneof_descriptor, value) 367 368 def _ListValueMessageToJsonObject(self, message): 369 """Converts ListValue message according to Proto3 JSON Specification.""" 370 return [self._ValueMessageToJsonObject(value) 371 for value in message.values] 372 373 def _StructMessageToJsonObject(self, message): 374 """Converts Struct message according to Proto3 JSON Specification.""" 375 fields = message.fields 376 ret = {} 377 for key in fields: 378 ret[key] = self._ValueMessageToJsonObject(fields[key]) 379 return ret 380 381 def _WrapperMessageToJsonObject(self, message): 382 return self._FieldToJsonObject( 383 message.DESCRIPTOR.fields_by_name['value'], message.value) 384 385 386def _IsWrapperMessage(message_descriptor): 387 return message_descriptor.file.name == 'google/protobuf/wrappers.proto' 388 389 390def _DuplicateChecker(js): 391 result = {} 392 for name, value in js: 393 if name in result: 394 raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name)) 395 result[name] = value 396 return result 397 398 399def _CreateMessageFromTypeUrl(type_url, descriptor_pool): 400 """Creates a message from a type URL.""" 401 db = symbol_database.Default() 402 pool = db.pool if descriptor_pool is None else descriptor_pool 403 type_name = type_url.split('/')[-1] 404 try: 405 message_descriptor = pool.FindMessageTypeByName(type_name) 406 except KeyError: 407 raise TypeError( 408 'Can not find message descriptor by type_url: {0}.'.format(type_url)) 409 message_class = db.GetPrototype(message_descriptor) 410 return message_class() 411 412 413def Parse(text, message, ignore_unknown_fields=False, descriptor_pool=None): 414 """Parses a JSON representation of a protocol message into a message. 415 416 Args: 417 text: Message JSON representation. 418 message: A protocol buffer message to merge into. 419 ignore_unknown_fields: If True, do not raise errors for unknown fields. 420 descriptor_pool: A Descriptor Pool for resolving types. If None use the 421 default. 422 423 Returns: 424 The same message passed as argument. 425 426 Raises:: 427 ParseError: On JSON parsing problems. 428 """ 429 if not isinstance(text, six.text_type): text = text.decode('utf-8') 430 try: 431 js = json.loads(text, object_pairs_hook=_DuplicateChecker) 432 except ValueError as e: 433 raise ParseError('Failed to load JSON: {0}.'.format(str(e))) 434 return ParseDict(js, message, ignore_unknown_fields, descriptor_pool) 435 436 437def ParseDict(js_dict, 438 message, 439 ignore_unknown_fields=False, 440 descriptor_pool=None): 441 """Parses a JSON dictionary representation into a message. 442 443 Args: 444 js_dict: Dict representation of a JSON message. 445 message: A protocol buffer message to merge into. 446 ignore_unknown_fields: If True, do not raise errors for unknown fields. 447 descriptor_pool: A Descriptor Pool for resolving types. If None use the 448 default. 449 450 Returns: 451 The same message passed as argument. 452 """ 453 parser = _Parser(ignore_unknown_fields, descriptor_pool) 454 parser.ConvertMessage(js_dict, message) 455 return message 456 457 458_INT_OR_FLOAT = six.integer_types + (float,) 459 460 461class _Parser(object): 462 """JSON format parser for protocol message.""" 463 464 def __init__(self, ignore_unknown_fields, descriptor_pool): 465 self.ignore_unknown_fields = ignore_unknown_fields 466 self.descriptor_pool = descriptor_pool 467 468 def ConvertMessage(self, value, message): 469 """Convert a JSON object into a message. 470 471 Args: 472 value: A JSON object. 473 message: A WKT or regular protocol message to record the data. 474 475 Raises: 476 ParseError: In case of convert problems. 477 """ 478 message_descriptor = message.DESCRIPTOR 479 full_name = message_descriptor.full_name 480 if _IsWrapperMessage(message_descriptor): 481 self._ConvertWrapperMessage(value, message) 482 elif full_name in _WKTJSONMETHODS: 483 methodcaller(_WKTJSONMETHODS[full_name][1], value, message)(self) 484 else: 485 self._ConvertFieldValuePair(value, message) 486 487 def _ConvertFieldValuePair(self, js, message): 488 """Convert field value pairs into regular message. 489 490 Args: 491 js: A JSON object to convert the field value pairs. 492 message: A regular protocol message to record the data. 493 494 Raises: 495 ParseError: In case of problems converting. 496 """ 497 names = [] 498 message_descriptor = message.DESCRIPTOR 499 fields_by_json_name = dict((f.json_name, f) 500 for f in message_descriptor.fields) 501 for name in js: 502 try: 503 field = fields_by_json_name.get(name, None) 504 if not field: 505 field = message_descriptor.fields_by_name.get(name, None) 506 if not field and _VALID_EXTENSION_NAME.match(name): 507 if not message_descriptor.is_extendable: 508 raise ParseError('Message type {0} does not have extensions'.format( 509 message_descriptor.full_name)) 510 identifier = name[1:-1] # strip [] brackets 511 # pylint: disable=protected-access 512 field = message.Extensions._FindExtensionByName(identifier) 513 # pylint: enable=protected-access 514 if not field: 515 # Try looking for extension by the message type name, dropping the 516 # field name following the final . separator in full_name. 517 identifier = '.'.join(identifier.split('.')[:-1]) 518 # pylint: disable=protected-access 519 field = message.Extensions._FindExtensionByName(identifier) 520 # pylint: enable=protected-access 521 if not field: 522 if self.ignore_unknown_fields: 523 continue 524 raise ParseError( 525 ('Message type "{0}" has no field named "{1}".\n' 526 ' Available Fields(except extensions): {2}').format( 527 message_descriptor.full_name, name, 528 [f.json_name for f in message_descriptor.fields])) 529 if name in names: 530 raise ParseError('Message type "{0}" should not have multiple ' 531 '"{1}" fields.'.format( 532 message.DESCRIPTOR.full_name, name)) 533 names.append(name) 534 # Check no other oneof field is parsed. 535 if field.containing_oneof is not None: 536 oneof_name = field.containing_oneof.name 537 if oneof_name in names: 538 raise ParseError('Message type "{0}" should not have multiple ' 539 '"{1}" oneof fields.'.format( 540 message.DESCRIPTOR.full_name, oneof_name)) 541 names.append(oneof_name) 542 543 value = js[name] 544 if value is None: 545 if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE 546 and field.message_type.full_name == 'google.protobuf.Value'): 547 sub_message = getattr(message, field.name) 548 sub_message.null_value = 0 549 elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM 550 and field.enum_type.full_name == 'google.protobuf.NullValue'): 551 setattr(message, field.name, 0) 552 else: 553 message.ClearField(field.name) 554 continue 555 556 # Parse field value. 557 if _IsMapEntry(field): 558 message.ClearField(field.name) 559 self._ConvertMapFieldValue(value, message, field) 560 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 561 message.ClearField(field.name) 562 if not isinstance(value, list): 563 raise ParseError('repeated field {0} must be in [] which is ' 564 '{1}.'.format(name, value)) 565 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 566 # Repeated message field. 567 for item in value: 568 sub_message = getattr(message, field.name).add() 569 # None is a null_value in Value. 570 if (item is None and 571 sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): 572 raise ParseError('null is not allowed to be used as an element' 573 ' in a repeated field.') 574 self.ConvertMessage(item, sub_message) 575 else: 576 # Repeated scalar field. 577 for item in value: 578 if item is None: 579 raise ParseError('null is not allowed to be used as an element' 580 ' in a repeated field.') 581 getattr(message, field.name).append( 582 _ConvertScalarFieldValue(item, field)) 583 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 584 if field.is_extension: 585 sub_message = message.Extensions[field] 586 else: 587 sub_message = getattr(message, field.name) 588 sub_message.SetInParent() 589 self.ConvertMessage(value, sub_message) 590 else: 591 if field.is_extension: 592 message.Extensions[field] = _ConvertScalarFieldValue(value, field) 593 else: 594 setattr(message, field.name, _ConvertScalarFieldValue(value, field)) 595 except ParseError as e: 596 if field and field.containing_oneof is None: 597 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) 598 else: 599 raise ParseError(str(e)) 600 except ValueError as e: 601 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) 602 except TypeError as e: 603 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) 604 605 def _ConvertAnyMessage(self, value, message): 606 """Convert a JSON representation into Any message.""" 607 if isinstance(value, dict) and not value: 608 return 609 try: 610 type_url = value['@type'] 611 except KeyError: 612 raise ParseError('@type is missing when parsing any message.') 613 614 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) 615 message_descriptor = sub_message.DESCRIPTOR 616 full_name = message_descriptor.full_name 617 if _IsWrapperMessage(message_descriptor): 618 self._ConvertWrapperMessage(value['value'], sub_message) 619 elif full_name in _WKTJSONMETHODS: 620 methodcaller( 621 _WKTJSONMETHODS[full_name][1], value['value'], sub_message)(self) 622 else: 623 del value['@type'] 624 self._ConvertFieldValuePair(value, sub_message) 625 value['@type'] = type_url 626 # Sets Any message 627 message.value = sub_message.SerializeToString() 628 message.type_url = type_url 629 630 def _ConvertGenericMessage(self, value, message): 631 """Convert a JSON representation into message with FromJsonString.""" 632 # Duration, Timestamp, FieldMask have a FromJsonString method to do the 633 # conversion. Users can also call the method directly. 634 try: 635 message.FromJsonString(value) 636 except ValueError as e: 637 raise ParseError(e) 638 639 def _ConvertValueMessage(self, value, message): 640 """Convert a JSON representation into Value message.""" 641 if isinstance(value, dict): 642 self._ConvertStructMessage(value, message.struct_value) 643 elif isinstance(value, list): 644 self. _ConvertListValueMessage(value, message.list_value) 645 elif value is None: 646 message.null_value = 0 647 elif isinstance(value, bool): 648 message.bool_value = value 649 elif isinstance(value, six.string_types): 650 message.string_value = value 651 elif isinstance(value, _INT_OR_FLOAT): 652 message.number_value = value 653 else: 654 raise ParseError('Value {0} has unexpected type {1}.'.format( 655 value, type(value))) 656 657 def _ConvertListValueMessage(self, value, message): 658 """Convert a JSON representation into ListValue message.""" 659 if not isinstance(value, list): 660 raise ParseError( 661 'ListValue must be in [] which is {0}.'.format(value)) 662 message.ClearField('values') 663 for item in value: 664 self._ConvertValueMessage(item, message.values.add()) 665 666 def _ConvertStructMessage(self, value, message): 667 """Convert a JSON representation into Struct message.""" 668 if not isinstance(value, dict): 669 raise ParseError( 670 'Struct must be in a dict which is {0}.'.format(value)) 671 # Clear will mark the struct as modified so it will be created even if 672 # there are no values. 673 message.Clear() 674 for key in value: 675 self._ConvertValueMessage(value[key], message.fields[key]) 676 return 677 678 def _ConvertWrapperMessage(self, value, message): 679 """Convert a JSON representation into Wrapper message.""" 680 field = message.DESCRIPTOR.fields_by_name['value'] 681 setattr(message, 'value', _ConvertScalarFieldValue(value, field)) 682 683 def _ConvertMapFieldValue(self, value, message, field): 684 """Convert map field value for a message map field. 685 686 Args: 687 value: A JSON object to convert the map field value. 688 message: A protocol message to record the converted data. 689 field: The descriptor of the map field to be converted. 690 691 Raises: 692 ParseError: In case of convert problems. 693 """ 694 if not isinstance(value, dict): 695 raise ParseError( 696 'Map field {0} must be in a dict which is {1}.'.format( 697 field.name, value)) 698 key_field = field.message_type.fields_by_name['key'] 699 value_field = field.message_type.fields_by_name['value'] 700 for key in value: 701 key_value = _ConvertScalarFieldValue(key, key_field, True) 702 if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 703 self.ConvertMessage(value[key], getattr( 704 message, field.name)[key_value]) 705 else: 706 getattr(message, field.name)[key_value] = _ConvertScalarFieldValue( 707 value[key], value_field) 708 709 710def _ConvertScalarFieldValue(value, field, require_str=False): 711 """Convert a single scalar field value. 712 713 Args: 714 value: A scalar value to convert the scalar field value. 715 field: The descriptor of the field to convert. 716 require_str: If True, the field value must be a str. 717 718 Returns: 719 The converted scalar field value 720 721 Raises: 722 ParseError: In case of convert problems. 723 """ 724 if field.cpp_type in _INT_TYPES: 725 return _ConvertInteger(value) 726 elif field.cpp_type in _FLOAT_TYPES: 727 return _ConvertFloat(value, field) 728 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 729 return _ConvertBool(value, require_str) 730 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 731 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: 732 if isinstance(value, six.text_type): 733 encoded = value.encode('utf-8') 734 else: 735 encoded = value 736 # Add extra padding '=' 737 padded_value = encoded + b'=' * (4 - len(encoded) % 4) 738 return base64.urlsafe_b64decode(padded_value) 739 else: 740 # Checking for unpaired surrogates appears to be unreliable, 741 # depending on the specific Python version, so we check manually. 742 if _UNPAIRED_SURROGATE_PATTERN.search(value): 743 raise ParseError('Unpaired surrogate') 744 return value 745 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 746 # Convert an enum value. 747 enum_value = field.enum_type.values_by_name.get(value, None) 748 if enum_value is None: 749 try: 750 number = int(value) 751 enum_value = field.enum_type.values_by_number.get(number, None) 752 except ValueError: 753 raise ParseError('Invalid enum value {0} for enum type {1}.'.format( 754 value, field.enum_type.full_name)) 755 if enum_value is None: 756 if field.file.syntax == 'proto3': 757 # Proto3 accepts unknown enums. 758 return number 759 raise ParseError('Invalid enum value {0} for enum type {1}.'.format( 760 value, field.enum_type.full_name)) 761 return enum_value.number 762 763 764def _ConvertInteger(value): 765 """Convert an integer. 766 767 Args: 768 value: A scalar value to convert. 769 770 Returns: 771 The integer value. 772 773 Raises: 774 ParseError: If an integer couldn't be consumed. 775 """ 776 if isinstance(value, float) and not value.is_integer(): 777 raise ParseError('Couldn\'t parse integer: {0}.'.format(value)) 778 779 if isinstance(value, six.text_type) and value.find(' ') != -1: 780 raise ParseError('Couldn\'t parse integer: "{0}".'.format(value)) 781 782 if isinstance(value, bool): 783 raise ParseError('Bool value {0} is not acceptable for ' 784 'integer field.'.format(value)) 785 786 return int(value) 787 788 789def _ConvertFloat(value, field): 790 """Convert an floating point number.""" 791 if isinstance(value, float): 792 if math.isnan(value): 793 raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead.') 794 if math.isinf(value): 795 if value > 0: 796 raise ParseError('Couldn\'t parse Infinity or value too large, ' 797 'use quoted "Infinity" instead.') 798 else: 799 raise ParseError('Couldn\'t parse -Infinity or value too small, ' 800 'use quoted "-Infinity" instead.') 801 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: 802 # pylint: disable=protected-access 803 if value > type_checkers._FLOAT_MAX: 804 raise ParseError('Float value too large') 805 # pylint: disable=protected-access 806 if value < type_checkers._FLOAT_MIN: 807 raise ParseError('Float value too small') 808 if value == 'nan': 809 raise ParseError('Couldn\'t parse float "nan", use "NaN" instead.') 810 try: 811 # Assume Python compatible syntax. 812 return float(value) 813 except ValueError: 814 # Check alternative spellings. 815 if value == _NEG_INFINITY: 816 return float('-inf') 817 elif value == _INFINITY: 818 return float('inf') 819 elif value == _NAN: 820 return float('nan') 821 else: 822 raise ParseError('Couldn\'t parse float: {0}.'.format(value)) 823 824 825def _ConvertBool(value, require_str): 826 """Convert a boolean value. 827 828 Args: 829 value: A scalar value to convert. 830 require_str: If True, value must be a str. 831 832 Returns: 833 The bool parsed. 834 835 Raises: 836 ParseError: If a boolean value couldn't be consumed. 837 """ 838 if require_str: 839 if value == 'true': 840 return True 841 elif value == 'false': 842 return False 843 else: 844 raise ParseError('Expected "true" or "false", not {0}.'.format(value)) 845 846 if not isinstance(value, bool): 847 raise ParseError('Expected true or false without quotes.') 848 return value 849 850_WKTJSONMETHODS = { 851 'google.protobuf.Any': ['_AnyMessageToJsonObject', 852 '_ConvertAnyMessage'], 853 'google.protobuf.Duration': ['_GenericMessageToJsonObject', 854 '_ConvertGenericMessage'], 855 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject', 856 '_ConvertGenericMessage'], 857 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject', 858 '_ConvertListValueMessage'], 859 'google.protobuf.Struct': ['_StructMessageToJsonObject', 860 '_ConvertStructMessage'], 861 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject', 862 '_ConvertGenericMessage'], 863 'google.protobuf.Value': ['_ValueMessageToJsonObject', 864 '_ConvertValueMessage'] 865} 866