| Index: third_party/protobuf/python/google/protobuf/json_format.py
|
| diff --git a/third_party/protobuf/python/google/protobuf/json_format.py b/third_party/protobuf/python/google/protobuf/json_format.py
|
| index 7921556e668d35f3be1d9cd14c210932771a6e0c..d02cb091563ed44122acc85d7a63901b2be2060a 100644
|
| --- a/third_party/protobuf/python/google/protobuf/json_format.py
|
| +++ b/third_party/protobuf/python/google/protobuf/json_format.py
|
| @@ -42,12 +42,18 @@ Simple usage example:
|
|
|
| __author__ = 'jieluo@google.com (Jie Luo)'
|
|
|
| +try:
|
| + from collections import OrderedDict
|
| +except ImportError:
|
| + from ordereddict import OrderedDict #PY26
|
| import base64
|
| import json
|
| import math
|
| +import re
|
| import six
|
| import sys
|
|
|
| +from operator import methodcaller
|
| from google.protobuf import descriptor
|
| from google.protobuf import symbol_database
|
|
|
| @@ -64,6 +70,9 @@ _INFINITY = 'Infinity'
|
| _NEG_INFINITY = '-Infinity'
|
| _NAN = 'NaN'
|
|
|
| +_UNPAIRED_SURROGATE_PATTERN = re.compile(six.u(
|
| + r'[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]'
|
| +))
|
|
|
| class Error(Exception):
|
| """Top-level module error for json_format."""
|
| @@ -77,7 +86,9 @@ class ParseError(Error):
|
| """Thrown in case of parsing error."""
|
|
|
|
|
| -def MessageToJson(message, including_default_value_fields=False):
|
| +def MessageToJson(message,
|
| + including_default_value_fields=False,
|
| + preserving_proto_field_name=False):
|
| """Converts protobuf message to JSON format.
|
|
|
| Args:
|
| @@ -86,26 +97,40 @@ def MessageToJson(message, including_default_value_fields=False):
|
| repeated fields, and map fields will always be serialized. If
|
| False, only serialize non-empty fields. Singular message fields
|
| and oneof fields are not affected by this option.
|
| + preserving_proto_field_name: If True, use the original proto field
|
| + names as defined in the .proto file. If False, convert the field
|
| + names to lowerCamelCase.
|
|
|
| Returns:
|
| A string containing the JSON formatted protocol buffer message.
|
| """
|
| - js = _MessageToJsonObject(message, including_default_value_fields)
|
| - return json.dumps(js, indent=2)
|
| + printer = _Printer(including_default_value_fields,
|
| + preserving_proto_field_name)
|
| + return printer.ToJsonString(message)
|
|
|
|
|
| -def _MessageToJsonObject(message, including_default_value_fields):
|
| - """Converts message to an object according to Proto3 JSON Specification."""
|
| - message_descriptor = message.DESCRIPTOR
|
| - full_name = message_descriptor.full_name
|
| - if _IsWrapperMessage(message_descriptor):
|
| - return _WrapperMessageToJsonObject(message)
|
| - if full_name in _WKTJSONMETHODS:
|
| - return _WKTJSONMETHODS[full_name][0](
|
| - message, including_default_value_fields)
|
| - js = {}
|
| - return _RegularMessageToJsonObject(
|
| - message, js, including_default_value_fields)
|
| +def MessageToDict(message,
|
| + including_default_value_fields=False,
|
| + preserving_proto_field_name=False):
|
| + """Converts protobuf message to a JSON dictionary.
|
| +
|
| + Args:
|
| + message: The protocol buffers message instance to serialize.
|
| + including_default_value_fields: If True, singular primitive fields,
|
| + repeated fields, and map fields will always be serialized. If
|
| + False, only serialize non-empty fields. Singular message fields
|
| + and oneof fields are not affected by this option.
|
| + preserving_proto_field_name: If True, use the original proto field
|
| + names as defined in the .proto file. If False, convert the field
|
| + names to lowerCamelCase.
|
| +
|
| + Returns:
|
| + A dict representation of the JSON formatted protocol buffer message.
|
| + """
|
| + printer = _Printer(including_default_value_fields,
|
| + preserving_proto_field_name)
|
| + # pylint: disable=protected-access
|
| + return printer._MessageToJsonObject(message)
|
|
|
|
|
| def _IsMapEntry(field):
|
| @@ -114,178 +139,187 @@ def _IsMapEntry(field):
|
| field.message_type.GetOptions().map_entry)
|
|
|
|
|
| -def _RegularMessageToJsonObject(message, js, including_default_value_fields):
|
| - """Converts normal message according to Proto3 JSON Specification."""
|
| - fields = message.ListFields()
|
| - include_default = including_default_value_fields
|
| +class _Printer(object):
|
| + """JSON format printer for protocol message."""
|
|
|
| - try:
|
| - for field, value in fields:
|
| - name = field.camelcase_name
|
| - if _IsMapEntry(field):
|
| - # Convert a map field.
|
| - v_field = field.message_type.fields_by_name['value']
|
| - js_map = {}
|
| - for key in value:
|
| - if isinstance(key, bool):
|
| - if key:
|
| - recorded_key = 'true'
|
| - else:
|
| - recorded_key = 'false'
|
| - else:
|
| - recorded_key = key
|
| - js_map[recorded_key] = _FieldToJsonObject(
|
| - v_field, value[key], including_default_value_fields)
|
| - js[name] = js_map
|
| - elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
|
| - # Convert a repeated field.
|
| - js[name] = [_FieldToJsonObject(field, k, include_default)
|
| - for k in value]
|
| - else:
|
| - js[name] = _FieldToJsonObject(field, value, include_default)
|
| -
|
| - # Serialize default value if including_default_value_fields is True.
|
| - if including_default_value_fields:
|
| - message_descriptor = message.DESCRIPTOR
|
| - for field in message_descriptor.fields:
|
| - # Singular message fields and oneof fields will not be affected.
|
| - if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and
|
| - field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or
|
| - field.containing_oneof):
|
| - continue
|
| - name = field.camelcase_name
|
| - if name in js:
|
| - # Skip the field which has been serailized already.
|
| - continue
|
| - if _IsMapEntry(field):
|
| - js[name] = {}
|
| - elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
|
| - js[name] = []
|
| - else:
|
| - js[name] = _FieldToJsonObject(field, field.default_value)
|
| + def __init__(self,
|
| + including_default_value_fields=False,
|
| + preserving_proto_field_name=False):
|
| + self.including_default_value_fields = including_default_value_fields
|
| + self.preserving_proto_field_name = preserving_proto_field_name
|
|
|
| - except ValueError as e:
|
| - raise SerializeToJsonError(
|
| - 'Failed to serialize {0} field: {1}.'.format(field.name, e))
|
| + def ToJsonString(self, message):
|
| + js = self._MessageToJsonObject(message)
|
| + return json.dumps(js, indent=2)
|
|
|
| - return js
|
| + def _MessageToJsonObject(self, message):
|
| + """Converts message to an object according to Proto3 JSON Specification."""
|
| + message_descriptor = message.DESCRIPTOR
|
| + full_name = message_descriptor.full_name
|
| + if _IsWrapperMessage(message_descriptor):
|
| + return self._WrapperMessageToJsonObject(message)
|
| + if full_name in _WKTJSONMETHODS:
|
| + return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self)
|
| + js = {}
|
| + return self._RegularMessageToJsonObject(message, js)
|
|
|
| + def _RegularMessageToJsonObject(self, message, js):
|
| + """Converts normal message according to Proto3 JSON Specification."""
|
| + fields = message.ListFields()
|
|
|
| -def _FieldToJsonObject(
|
| - field, value, including_default_value_fields=False):
|
| - """Converts field value according to Proto3 JSON Specification."""
|
| - if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| - return _MessageToJsonObject(value, including_default_value_fields)
|
| - elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
|
| - enum_value = field.enum_type.values_by_number.get(value, None)
|
| - if enum_value is not None:
|
| - return enum_value.name
|
| - else:
|
| - raise SerializeToJsonError('Enum field contains an integer value '
|
| - 'which can not mapped to an enum value.')
|
| - elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
|
| - if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
|
| - # Use base64 Data encoding for bytes
|
| - return base64.b64encode(value).decode('utf-8')
|
| - else:
|
| - return value
|
| - elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
|
| - return bool(value)
|
| - elif field.cpp_type in _INT64_TYPES:
|
| - return str(value)
|
| - elif field.cpp_type in _FLOAT_TYPES:
|
| - if math.isinf(value):
|
| - if value < 0.0:
|
| - return _NEG_INFINITY
|
| - else:
|
| - return _INFINITY
|
| - if math.isnan(value):
|
| - return _NAN
|
| - return value
|
| + try:
|
| + for field, value in fields:
|
| + if self.preserving_proto_field_name:
|
| + name = field.name
|
| + else:
|
| + name = field.json_name
|
| + if _IsMapEntry(field):
|
| + # Convert a map field.
|
| + v_field = field.message_type.fields_by_name['value']
|
| + js_map = {}
|
| + for key in value:
|
| + if isinstance(key, bool):
|
| + if key:
|
| + recorded_key = 'true'
|
| + else:
|
| + recorded_key = 'false'
|
| + else:
|
| + recorded_key = key
|
| + js_map[recorded_key] = self._FieldToJsonObject(
|
| + v_field, value[key])
|
| + js[name] = js_map
|
| + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
|
| + # Convert a repeated field.
|
| + js[name] = [self._FieldToJsonObject(field, k)
|
| + for k in value]
|
| + else:
|
| + js[name] = self._FieldToJsonObject(field, value)
|
| +
|
| + # Serialize default value if including_default_value_fields is True.
|
| + if self.including_default_value_fields:
|
| + message_descriptor = message.DESCRIPTOR
|
| + for field in message_descriptor.fields:
|
| + # Singular message fields and oneof fields will not be affected.
|
| + if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and
|
| + field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or
|
| + field.containing_oneof):
|
| + continue
|
| + if self.preserving_proto_field_name:
|
| + name = field.name
|
| + else:
|
| + name = field.json_name
|
| + if name in js:
|
| + # Skip the field which has been serailized already.
|
| + continue
|
| + if _IsMapEntry(field):
|
| + js[name] = {}
|
| + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
|
| + js[name] = []
|
| + else:
|
| + js[name] = self._FieldToJsonObject(field, field.default_value)
|
|
|
| + except ValueError as e:
|
| + raise SerializeToJsonError(
|
| + 'Failed to serialize {0} field: {1}.'.format(field.name, e))
|
|
|
| -def _AnyMessageToJsonObject(message, including_default):
|
| - """Converts Any message according to Proto3 JSON Specification."""
|
| - if not message.ListFields():
|
| - return {}
|
| - js = {}
|
| - type_url = message.type_url
|
| - js['@type'] = type_url
|
| - sub_message = _CreateMessageFromTypeUrl(type_url)
|
| - sub_message.ParseFromString(message.value)
|
| - message_descriptor = sub_message.DESCRIPTOR
|
| - full_name = message_descriptor.full_name
|
| - if _IsWrapperMessage(message_descriptor):
|
| - js['value'] = _WrapperMessageToJsonObject(sub_message)
|
| return js
|
| - if full_name in _WKTJSONMETHODS:
|
| - js['value'] = _WKTJSONMETHODS[full_name][0](sub_message, including_default)
|
| - return js
|
| - return _RegularMessageToJsonObject(sub_message, js, including_default)
|
| -
|
| -
|
| -def _CreateMessageFromTypeUrl(type_url):
|
| - # TODO(jieluo): Should add a way that users can register the type resolver
|
| - # instead of the default one.
|
| - db = symbol_database.Default()
|
| - type_name = type_url.split('/')[-1]
|
| - try:
|
| - message_descriptor = db.pool.FindMessageTypeByName(type_name)
|
| - except KeyError:
|
| - raise TypeError(
|
| - 'Can not find message descriptor by type_url: {0}.'.format(type_url))
|
| - message_class = db.GetPrototype(message_descriptor)
|
| - return message_class()
|
| -
|
| -
|
| -def _GenericMessageToJsonObject(message, unused_including_default):
|
| - """Converts message by ToJsonString according to Proto3 JSON Specification."""
|
| - # Duration, Timestamp and FieldMask have ToJsonString method to do the
|
| - # convert. Users can also call the method directly.
|
| - return message.ToJsonString()
|
| -
|
| -
|
| -def _ValueMessageToJsonObject(message, unused_including_default=False):
|
| - """Converts Value message according to Proto3 JSON Specification."""
|
| - which = message.WhichOneof('kind')
|
| - # If the Value message is not set treat as null_value when serialize
|
| - # to JSON. The parse back result will be different from original message.
|
| - if which is None or which == 'null_value':
|
| - return None
|
| - if which == 'list_value':
|
| - return _ListValueMessageToJsonObject(message.list_value)
|
| - if which == 'struct_value':
|
| - value = message.struct_value
|
| - else:
|
| - value = getattr(message, which)
|
| - oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]
|
| - return _FieldToJsonObject(oneof_descriptor, value)
|
|
|
| + def _FieldToJsonObject(self, field, value):
|
| + """Converts field value according to Proto3 JSON Specification."""
|
| + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| + return self._MessageToJsonObject(value)
|
| + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
|
| + enum_value = field.enum_type.values_by_number.get(value, None)
|
| + if enum_value is not None:
|
| + return enum_value.name
|
| + else:
|
| + raise SerializeToJsonError('Enum field contains an integer value '
|
| + 'which can not mapped to an enum value.')
|
| + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
|
| + if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
|
| + # Use base64 Data encoding for bytes
|
| + return base64.b64encode(value).decode('utf-8')
|
| + else:
|
| + return value
|
| + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
|
| + return bool(value)
|
| + elif field.cpp_type in _INT64_TYPES:
|
| + return str(value)
|
| + elif field.cpp_type in _FLOAT_TYPES:
|
| + if math.isinf(value):
|
| + if value < 0.0:
|
| + return _NEG_INFINITY
|
| + else:
|
| + return _INFINITY
|
| + if math.isnan(value):
|
| + return _NAN
|
| + return value
|
| +
|
| + def _AnyMessageToJsonObject(self, message):
|
| + """Converts Any message according to Proto3 JSON Specification."""
|
| + if not message.ListFields():
|
| + return {}
|
| + # Must print @type first, use OrderedDict instead of {}
|
| + js = OrderedDict()
|
| + type_url = message.type_url
|
| + js['@type'] = type_url
|
| + sub_message = _CreateMessageFromTypeUrl(type_url)
|
| + sub_message.ParseFromString(message.value)
|
| + message_descriptor = sub_message.DESCRIPTOR
|
| + full_name = message_descriptor.full_name
|
| + if _IsWrapperMessage(message_descriptor):
|
| + js['value'] = self._WrapperMessageToJsonObject(sub_message)
|
| + return js
|
| + if full_name in _WKTJSONMETHODS:
|
| + js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0],
|
| + sub_message)(self)
|
| + return js
|
| + return self._RegularMessageToJsonObject(sub_message, js)
|
| +
|
| + def _GenericMessageToJsonObject(self, message):
|
| + """Converts message according to Proto3 JSON Specification."""
|
| + # Duration, Timestamp and FieldMask have ToJsonString method to do the
|
| + # convert. Users can also call the method directly.
|
| + return message.ToJsonString()
|
| +
|
| + def _ValueMessageToJsonObject(self, message):
|
| + """Converts Value message according to Proto3 JSON Specification."""
|
| + which = message.WhichOneof('kind')
|
| + # If the Value message is not set treat as null_value when serialize
|
| + # to JSON. The parse back result will be different from original message.
|
| + if which is None or which == 'null_value':
|
| + return None
|
| + if which == 'list_value':
|
| + return self._ListValueMessageToJsonObject(message.list_value)
|
| + if which == 'struct_value':
|
| + value = message.struct_value
|
| + else:
|
| + value = getattr(message, which)
|
| + oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]
|
| + return self._FieldToJsonObject(oneof_descriptor, value)
|
|
|
| -def _ListValueMessageToJsonObject(message, unused_including_default=False):
|
| - """Converts ListValue message according to Proto3 JSON Specification."""
|
| - return [_ValueMessageToJsonObject(value)
|
| - for value in message.values]
|
| + def _ListValueMessageToJsonObject(self, message):
|
| + """Converts ListValue message according to Proto3 JSON Specification."""
|
| + return [self._ValueMessageToJsonObject(value)
|
| + for value in message.values]
|
|
|
| + def _StructMessageToJsonObject(self, message):
|
| + """Converts Struct message according to Proto3 JSON Specification."""
|
| + fields = message.fields
|
| + ret = {}
|
| + for key in fields:
|
| + ret[key] = self._ValueMessageToJsonObject(fields[key])
|
| + return ret
|
|
|
| -def _StructMessageToJsonObject(message, unused_including_default=False):
|
| - """Converts Struct message according to Proto3 JSON Specification."""
|
| - fields = message.fields
|
| - ret = {}
|
| - for key in fields:
|
| - ret[key] = _ValueMessageToJsonObject(fields[key])
|
| - return ret
|
| + def _WrapperMessageToJsonObject(self, message):
|
| + return self._FieldToJsonObject(
|
| + message.DESCRIPTOR.fields_by_name['value'], message.value)
|
|
|
|
|
| def _IsWrapperMessage(message_descriptor):
|
| return message_descriptor.file.name == 'google/protobuf/wrappers.proto'
|
|
|
|
|
| -def _WrapperMessageToJsonObject(message):
|
| - return _FieldToJsonObject(
|
| - message.DESCRIPTOR.fields_by_name['value'], message.value)
|
| -
|
| -
|
| def _DuplicateChecker(js):
|
| result = {}
|
| for name, value in js:
|
| @@ -295,12 +329,27 @@ def _DuplicateChecker(js):
|
| return result
|
|
|
|
|
| -def Parse(text, message):
|
| +def _CreateMessageFromTypeUrl(type_url):
|
| + # TODO(jieluo): Should add a way that users can register the type resolver
|
| + # instead of the default one.
|
| + db = symbol_database.Default()
|
| + type_name = type_url.split('/')[-1]
|
| + try:
|
| + message_descriptor = db.pool.FindMessageTypeByName(type_name)
|
| + except KeyError:
|
| + raise TypeError(
|
| + 'Can not find message descriptor by type_url: {0}.'.format(type_url))
|
| + message_class = db.GetPrototype(message_descriptor)
|
| + return message_class()
|
| +
|
| +
|
| +def Parse(text, message, ignore_unknown_fields=False):
|
| """Parses a JSON representation of a protocol message into a message.
|
|
|
| Args:
|
| text: Message JSON representation.
|
| - message: A protocol beffer message to merge into.
|
| + message: A protocol buffer message to merge into.
|
| + ignore_unknown_fields: If True, do not raise errors for unknown fields.
|
|
|
| Returns:
|
| The same message passed as argument.
|
| @@ -317,213 +366,241 @@ def Parse(text, message):
|
| js = json.loads(text, object_pairs_hook=_DuplicateChecker)
|
| except ValueError as e:
|
| raise ParseError('Failed to load JSON: {0}.'.format(str(e)))
|
| - _ConvertMessage(js, message)
|
| - return message
|
| + return ParseDict(js, message, ignore_unknown_fields)
|
|
|
|
|
| -def _ConvertFieldValuePair(js, message):
|
| - """Convert field value pairs into regular message.
|
| +def ParseDict(js_dict, message, ignore_unknown_fields=False):
|
| + """Parses a JSON dictionary representation into a message.
|
|
|
| Args:
|
| - js: A JSON object to convert the field value pairs.
|
| - message: A regular protocol message to record the data.
|
| + js_dict: Dict representation of a JSON message.
|
| + message: A protocol buffer message to merge into.
|
| + ignore_unknown_fields: If True, do not raise errors for unknown fields.
|
|
|
| - Raises:
|
| - ParseError: In case of problems converting.
|
| + Returns:
|
| + The same message passed as argument.
|
| """
|
| - names = []
|
| - message_descriptor = message.DESCRIPTOR
|
| - for name in js:
|
| - try:
|
| - field = message_descriptor.fields_by_camelcase_name.get(name, None)
|
| - if not field:
|
| - raise ParseError(
|
| - 'Message type "{0}" has no field named "{1}".'.format(
|
| - message_descriptor.full_name, name))
|
| - if name in names:
|
| - raise ParseError(
|
| - 'Message type "{0}" should not have multiple "{1}" fields.'.format(
|
| - message.DESCRIPTOR.full_name, name))
|
| - names.append(name)
|
| - # Check no other oneof field is parsed.
|
| - if field.containing_oneof is not None:
|
| - oneof_name = field.containing_oneof.name
|
| - if oneof_name in names:
|
| - raise ParseError('Message type "{0}" should not have multiple "{1}" '
|
| - 'oneof fields.'.format(
|
| - message.DESCRIPTOR.full_name, oneof_name))
|
| - names.append(oneof_name)
|
| -
|
| - value = js[name]
|
| - if value is None:
|
| - message.ClearField(field.name)
|
| - continue
|
| -
|
| - # Parse field value.
|
| - if _IsMapEntry(field):
|
| - message.ClearField(field.name)
|
| - _ConvertMapFieldValue(value, message, field)
|
| - elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
|
| - message.ClearField(field.name)
|
| - if not isinstance(value, list):
|
| - raise ParseError('repeated field {0} must be in [] which is '
|
| - '{1}.'.format(name, value))
|
| - if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| - # Repeated message field.
|
| - for item in value:
|
| - sub_message = getattr(message, field.name).add()
|
| - # None is a null_value in Value.
|
| - if (item is None and
|
| - sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'):
|
| - raise ParseError('null is not allowed to be used as an element'
|
| - ' in a repeated field.')
|
| - _ConvertMessage(item, sub_message)
|
| - else:
|
| - # Repeated scalar field.
|
| - for item in value:
|
| - if item is None:
|
| - raise ParseError('null is not allowed to be used as an element'
|
| - ' in a repeated field.')
|
| - getattr(message, field.name).append(
|
| - _ConvertScalarFieldValue(item, field))
|
| - elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| - sub_message = getattr(message, field.name)
|
| - _ConvertMessage(value, sub_message)
|
| - else:
|
| - setattr(message, field.name, _ConvertScalarFieldValue(value, field))
|
| - except ParseError as e:
|
| - if field and field.containing_oneof is None:
|
| - raise ParseError('Failed to parse {0} field: {1}'.format(name, e))
|
| - else:
|
| - raise ParseError(str(e))
|
| - except ValueError as e:
|
| - raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
|
| - except TypeError as e:
|
| - raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
|
| + parser = _Parser(ignore_unknown_fields)
|
| + parser.ConvertMessage(js_dict, message)
|
| + return message
|
|
|
|
|
| -def _ConvertMessage(value, message):
|
| - """Convert a JSON object into a message.
|
| +_INT_OR_FLOAT = six.integer_types + (float,)
|
|
|
| - Args:
|
| - value: A JSON object.
|
| - message: A WKT or regular protocol message to record the data.
|
| -
|
| - Raises:
|
| - ParseError: In case of convert problems.
|
| - """
|
| - message_descriptor = message.DESCRIPTOR
|
| - full_name = message_descriptor.full_name
|
| - if _IsWrapperMessage(message_descriptor):
|
| - _ConvertWrapperMessage(value, message)
|
| - elif full_name in _WKTJSONMETHODS:
|
| - _WKTJSONMETHODS[full_name][1](value, message)
|
| - else:
|
| - _ConvertFieldValuePair(value, message)
|
| -
|
| -
|
| -def _ConvertAnyMessage(value, message):
|
| - """Convert a JSON representation into Any message."""
|
| - if isinstance(value, dict) and not value:
|
| - return
|
| - try:
|
| - type_url = value['@type']
|
| - except KeyError:
|
| - raise ParseError('@type is missing when parsing any message.')
|
| -
|
| - sub_message = _CreateMessageFromTypeUrl(type_url)
|
| - message_descriptor = sub_message.DESCRIPTOR
|
| - full_name = message_descriptor.full_name
|
| - if _IsWrapperMessage(message_descriptor):
|
| - _ConvertWrapperMessage(value['value'], sub_message)
|
| - elif full_name in _WKTJSONMETHODS:
|
| - _WKTJSONMETHODS[full_name][1](value['value'], sub_message)
|
| - else:
|
| - del value['@type']
|
| - _ConvertFieldValuePair(value, sub_message)
|
| - # Sets Any message
|
| - message.value = sub_message.SerializeToString()
|
| - message.type_url = type_url
|
| -
|
| -
|
| -def _ConvertGenericMessage(value, message):
|
| - """Convert a JSON representation into message with FromJsonString."""
|
| - # Durantion, Timestamp, FieldMask have FromJsonString method to do the
|
| - # convert. Users can also call the method directly.
|
| - message.FromJsonString(value)
|
|
|
| +class _Parser(object):
|
| + """JSON format parser for protocol message."""
|
|
|
| -_INT_OR_FLOAT = six.integer_types + (float,)
|
| + def __init__(self,
|
| + ignore_unknown_fields):
|
| + self.ignore_unknown_fields = ignore_unknown_fields
|
|
|
| + def ConvertMessage(self, value, message):
|
| + """Convert a JSON object into a message.
|
|
|
| -def _ConvertValueMessage(value, message):
|
| - """Convert a JSON representation into Value message."""
|
| - if isinstance(value, dict):
|
| - _ConvertStructMessage(value, message.struct_value)
|
| - elif isinstance(value, list):
|
| - _ConvertListValueMessage(value, message.list_value)
|
| - elif value is None:
|
| - message.null_value = 0
|
| - elif isinstance(value, bool):
|
| - message.bool_value = value
|
| - elif isinstance(value, six.string_types):
|
| - message.string_value = value
|
| - elif isinstance(value, _INT_OR_FLOAT):
|
| - message.number_value = value
|
| - else:
|
| - raise ParseError('Unexpected type for Value message.')
|
| -
|
| -
|
| -def _ConvertListValueMessage(value, message):
|
| - """Convert a JSON representation into ListValue message."""
|
| - if not isinstance(value, list):
|
| - raise ParseError(
|
| - 'ListValue must be in [] which is {0}.'.format(value))
|
| - message.ClearField('values')
|
| - for item in value:
|
| - _ConvertValueMessage(item, message.values.add())
|
| -
|
| -
|
| -def _ConvertStructMessage(value, message):
|
| - """Convert a JSON representation into Struct message."""
|
| - if not isinstance(value, dict):
|
| - raise ParseError(
|
| - 'Struct must be in a dict which is {0}.'.format(value))
|
| - for key in value:
|
| - _ConvertValueMessage(value[key], message.fields[key])
|
| - return
|
| -
|
| -
|
| -def _ConvertWrapperMessage(value, message):
|
| - """Convert a JSON representation into Wrapper message."""
|
| - field = message.DESCRIPTOR.fields_by_name['value']
|
| - setattr(message, 'value', _ConvertScalarFieldValue(value, field))
|
| -
|
| -
|
| -def _ConvertMapFieldValue(value, message, field):
|
| - """Convert map field value for a message map field.
|
| + Args:
|
| + value: A JSON object.
|
| + message: A WKT or regular protocol message to record the data.
|
|
|
| - Args:
|
| - value: A JSON object to convert the map field value.
|
| - message: A protocol message to record the converted data.
|
| - field: The descriptor of the map field to be converted.
|
| + Raises:
|
| + ParseError: In case of convert problems.
|
| + """
|
| + message_descriptor = message.DESCRIPTOR
|
| + full_name = message_descriptor.full_name
|
| + if _IsWrapperMessage(message_descriptor):
|
| + self._ConvertWrapperMessage(value, message)
|
| + elif full_name in _WKTJSONMETHODS:
|
| + methodcaller(_WKTJSONMETHODS[full_name][1], value, message)(self)
|
| + else:
|
| + self._ConvertFieldValuePair(value, message)
|
| +
|
| + def _ConvertFieldValuePair(self, js, message):
|
| + """Convert field value pairs into regular message.
|
| +
|
| + Args:
|
| + js: A JSON object to convert the field value pairs.
|
| + message: A regular protocol message to record the data.
|
| +
|
| + Raises:
|
| + ParseError: In case of problems converting.
|
| + """
|
| + names = []
|
| + message_descriptor = message.DESCRIPTOR
|
| + fields_by_json_name = dict((f.json_name, f)
|
| + for f in message_descriptor.fields)
|
| + for name in js:
|
| + try:
|
| + field = fields_by_json_name.get(name, None)
|
| + if not field:
|
| + field = message_descriptor.fields_by_name.get(name, None)
|
| + if not field:
|
| + if self.ignore_unknown_fields:
|
| + continue
|
| + raise ParseError(
|
| + 'Message type "{0}" has no field named "{1}".'.format(
|
| + message_descriptor.full_name, name))
|
| + if name in names:
|
| + raise ParseError('Message type "{0}" should not have multiple '
|
| + '"{1}" fields.'.format(
|
| + message.DESCRIPTOR.full_name, name))
|
| + names.append(name)
|
| + # Check no other oneof field is parsed.
|
| + if field.containing_oneof is not None:
|
| + oneof_name = field.containing_oneof.name
|
| + if oneof_name in names:
|
| + raise ParseError('Message type "{0}" should not have multiple '
|
| + '"{1}" oneof fields.'.format(
|
| + message.DESCRIPTOR.full_name, oneof_name))
|
| + names.append(oneof_name)
|
| +
|
| + value = js[name]
|
| + if value is None:
|
| + if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE
|
| + and field.message_type.full_name == 'google.protobuf.Value'):
|
| + sub_message = getattr(message, field.name)
|
| + sub_message.null_value = 0
|
| + else:
|
| + message.ClearField(field.name)
|
| + continue
|
|
|
| - Raises:
|
| - ParseError: In case of convert problems.
|
| - """
|
| - if not isinstance(value, dict):
|
| - raise ParseError(
|
| - 'Map field {0} must be in a dict which is {1}.'.format(
|
| - field.name, value))
|
| - key_field = field.message_type.fields_by_name['key']
|
| - value_field = field.message_type.fields_by_name['value']
|
| - for key in value:
|
| - key_value = _ConvertScalarFieldValue(key, key_field, True)
|
| - if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| - _ConvertMessage(value[key], getattr(message, field.name)[key_value])
|
| + # Parse field value.
|
| + if _IsMapEntry(field):
|
| + message.ClearField(field.name)
|
| + self._ConvertMapFieldValue(value, message, field)
|
| + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
|
| + message.ClearField(field.name)
|
| + if not isinstance(value, list):
|
| + raise ParseError('repeated field {0} must be in [] which is '
|
| + '{1}.'.format(name, value))
|
| + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| + # Repeated message field.
|
| + for item in value:
|
| + sub_message = getattr(message, field.name).add()
|
| + # None is a null_value in Value.
|
| + if (item is None and
|
| + sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'):
|
| + raise ParseError('null is not allowed to be used as an element'
|
| + ' in a repeated field.')
|
| + self.ConvertMessage(item, sub_message)
|
| + else:
|
| + # Repeated scalar field.
|
| + for item in value:
|
| + if item is None:
|
| + raise ParseError('null is not allowed to be used as an element'
|
| + ' in a repeated field.')
|
| + getattr(message, field.name).append(
|
| + _ConvertScalarFieldValue(item, field))
|
| + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| + sub_message = getattr(message, field.name)
|
| + sub_message.SetInParent()
|
| + self.ConvertMessage(value, sub_message)
|
| + else:
|
| + setattr(message, field.name, _ConvertScalarFieldValue(value, field))
|
| + except ParseError as e:
|
| + if field and field.containing_oneof is None:
|
| + raise ParseError('Failed to parse {0} field: {1}'.format(name, e))
|
| + else:
|
| + raise ParseError(str(e))
|
| + except ValueError as e:
|
| + raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
|
| + except TypeError as e:
|
| + raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
|
| +
|
| + def _ConvertAnyMessage(self, value, message):
|
| + """Convert a JSON representation into Any message."""
|
| + if isinstance(value, dict) and not value:
|
| + return
|
| + try:
|
| + type_url = value['@type']
|
| + except KeyError:
|
| + raise ParseError('@type is missing when parsing any message.')
|
| +
|
| + sub_message = _CreateMessageFromTypeUrl(type_url)
|
| + message_descriptor = sub_message.DESCRIPTOR
|
| + full_name = message_descriptor.full_name
|
| + if _IsWrapperMessage(message_descriptor):
|
| + self._ConvertWrapperMessage(value['value'], sub_message)
|
| + elif full_name in _WKTJSONMETHODS:
|
| + methodcaller(
|
| + _WKTJSONMETHODS[full_name][1], value['value'], sub_message)(self)
|
| + else:
|
| + del value['@type']
|
| + self._ConvertFieldValuePair(value, sub_message)
|
| + # Sets Any message
|
| + message.value = sub_message.SerializeToString()
|
| + message.type_url = type_url
|
| +
|
| + def _ConvertGenericMessage(self, value, message):
|
| + """Convert a JSON representation into message with FromJsonString."""
|
| + # Durantion, Timestamp, FieldMask have FromJsonString method to do the
|
| + # convert. Users can also call the method directly.
|
| + message.FromJsonString(value)
|
| +
|
| + def _ConvertValueMessage(self, value, message):
|
| + """Convert a JSON representation into Value message."""
|
| + if isinstance(value, dict):
|
| + self._ConvertStructMessage(value, message.struct_value)
|
| + elif isinstance(value, list):
|
| + self. _ConvertListValueMessage(value, message.list_value)
|
| + elif value is None:
|
| + message.null_value = 0
|
| + elif isinstance(value, bool):
|
| + message.bool_value = value
|
| + elif isinstance(value, six.string_types):
|
| + message.string_value = value
|
| + elif isinstance(value, _INT_OR_FLOAT):
|
| + message.number_value = value
|
| else:
|
| - getattr(message, field.name)[key_value] = _ConvertScalarFieldValue(
|
| - value[key], value_field)
|
| + raise ParseError('Unexpected type for Value message.')
|
| +
|
| + def _ConvertListValueMessage(self, value, message):
|
| + """Convert a JSON representation into ListValue message."""
|
| + if not isinstance(value, list):
|
| + raise ParseError(
|
| + 'ListValue must be in [] which is {0}.'.format(value))
|
| + message.ClearField('values')
|
| + for item in value:
|
| + self._ConvertValueMessage(item, message.values.add())
|
| +
|
| + def _ConvertStructMessage(self, value, message):
|
| + """Convert a JSON representation into Struct message."""
|
| + if not isinstance(value, dict):
|
| + raise ParseError(
|
| + 'Struct must be in a dict which is {0}.'.format(value))
|
| + for key in value:
|
| + self._ConvertValueMessage(value[key], message.fields[key])
|
| + return
|
| +
|
| + def _ConvertWrapperMessage(self, value, message):
|
| + """Convert a JSON representation into Wrapper message."""
|
| + field = message.DESCRIPTOR.fields_by_name['value']
|
| + setattr(message, 'value', _ConvertScalarFieldValue(value, field))
|
| +
|
| + def _ConvertMapFieldValue(self, value, message, field):
|
| + """Convert map field value for a message map field.
|
| +
|
| + Args:
|
| + value: A JSON object to convert the map field value.
|
| + message: A protocol message to record the converted data.
|
| + field: The descriptor of the map field to be converted.
|
| +
|
| + Raises:
|
| + ParseError: In case of convert problems.
|
| + """
|
| + if not isinstance(value, dict):
|
| + raise ParseError(
|
| + 'Map field {0} must be in a dict which is {1}.'.format(
|
| + field.name, value))
|
| + key_field = field.message_type.fields_by_name['key']
|
| + value_field = field.message_type.fields_by_name['value']
|
| + for key in value:
|
| + key_value = _ConvertScalarFieldValue(key, key_field, True)
|
| + if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| + self.ConvertMessage(value[key], getattr(
|
| + message, field.name)[key_value])
|
| + else:
|
| + getattr(message, field.name)[key_value] = _ConvertScalarFieldValue(
|
| + value[key], value_field)
|
|
|
|
|
| def _ConvertScalarFieldValue(value, field, require_str=False):
|
| @@ -550,15 +627,24 @@ def _ConvertScalarFieldValue(value, field, require_str=False):
|
| if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
|
| return base64.b64decode(value)
|
| else:
|
| + # Checking for unpaired surrogates appears to be unreliable,
|
| + # depending on the specific Python version, so we check manually.
|
| + if _UNPAIRED_SURROGATE_PATTERN.search(value):
|
| + raise ParseError('Unpaired surrogate')
|
| return value
|
| elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
|
| # Convert an enum value.
|
| enum_value = field.enum_type.values_by_name.get(value, None)
|
| if enum_value is None:
|
| - raise ParseError(
|
| - 'Enum value must be a string literal with double quotes. '
|
| - 'Type "{0}" has no value named {1}.'.format(
|
| - field.enum_type.full_name, value))
|
| + try:
|
| + number = int(value)
|
| + enum_value = field.enum_type.values_by_number.get(number, None)
|
| + except ValueError:
|
| + raise ParseError('Invalid enum value {0} for enum type {1}.'.format(
|
| + value, field.enum_type.full_name))
|
| + if enum_value is None:
|
| + raise ParseError('Invalid enum value {0} for enum type {1}.'.format(
|
| + value, field.enum_type.full_name))
|
| return enum_value.number
|
|
|
|
|
| @@ -574,7 +660,7 @@ def _ConvertInteger(value):
|
| Raises:
|
| ParseError: If an integer couldn't be consumed.
|
| """
|
| - if isinstance(value, float):
|
| + if isinstance(value, float) and not value.is_integer():
|
| raise ParseError('Couldn\'t parse integer: {0}.'.format(value))
|
|
|
| if isinstance(value, six.text_type) and value.find(' ') != -1:
|
| @@ -628,18 +714,18 @@ def _ConvertBool(value, require_str):
|
| return value
|
|
|
| _WKTJSONMETHODS = {
|
| - 'google.protobuf.Any': [_AnyMessageToJsonObject,
|
| - _ConvertAnyMessage],
|
| - 'google.protobuf.Duration': [_GenericMessageToJsonObject,
|
| - _ConvertGenericMessage],
|
| - 'google.protobuf.FieldMask': [_GenericMessageToJsonObject,
|
| - _ConvertGenericMessage],
|
| - 'google.protobuf.ListValue': [_ListValueMessageToJsonObject,
|
| - _ConvertListValueMessage],
|
| - 'google.protobuf.Struct': [_StructMessageToJsonObject,
|
| - _ConvertStructMessage],
|
| - 'google.protobuf.Timestamp': [_GenericMessageToJsonObject,
|
| - _ConvertGenericMessage],
|
| - 'google.protobuf.Value': [_ValueMessageToJsonObject,
|
| - _ConvertValueMessage]
|
| + 'google.protobuf.Any': ['_AnyMessageToJsonObject',
|
| + '_ConvertAnyMessage'],
|
| + 'google.protobuf.Duration': ['_GenericMessageToJsonObject',
|
| + '_ConvertGenericMessage'],
|
| + 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject',
|
| + '_ConvertGenericMessage'],
|
| + 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject',
|
| + '_ConvertListValueMessage'],
|
| + 'google.protobuf.Struct': ['_StructMessageToJsonObject',
|
| + '_ConvertStructMessage'],
|
| + 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject',
|
| + '_ConvertGenericMessage'],
|
| + 'google.protobuf.Value': ['_ValueMessageToJsonObject',
|
| + '_ConvertValueMessage']
|
| }
|
|
|