| 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 cb76e1167711fbe57930d32120d99d5013b30add..7921556e668d35f3be1d9cd14c210932771a6e0c 100644
|
| --- a/third_party/protobuf/python/google/protobuf/json_format.py
|
| +++ b/third_party/protobuf/python/google/protobuf/json_format.py
|
| @@ -45,10 +45,11 @@ __author__ = 'jieluo@google.com (Jie Luo)'
|
| import base64
|
| import json
|
| import math
|
| -from six import text_type
|
| +import six
|
| import sys
|
|
|
| from google.protobuf import descriptor
|
| +from google.protobuf import symbol_database
|
|
|
| _TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S'
|
| _INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32,
|
| @@ -96,11 +97,15 @@ def MessageToJson(message, including_default_value_fields=False):
|
| def _MessageToJsonObject(message, including_default_value_fields):
|
| """Converts message to an object according to Proto3 JSON Specification."""
|
| message_descriptor = message.DESCRIPTOR
|
| - if hasattr(message, 'ToJsonString'):
|
| - return message.ToJsonString()
|
| + full_name = message_descriptor.full_name
|
| if _IsWrapperMessage(message_descriptor):
|
| return _WrapperMessageToJsonObject(message)
|
| - return _RegularMessageToJsonObject(message, including_default_value_fields)
|
| + 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 _IsMapEntry(field):
|
| @@ -109,9 +114,8 @@ def _IsMapEntry(field):
|
| field.message_type.GetOptions().map_entry)
|
|
|
|
|
| -def _RegularMessageToJsonObject(message, including_default_value_fields):
|
| +def _RegularMessageToJsonObject(message, js, including_default_value_fields):
|
| """Converts normal message according to Proto3 JSON Specification."""
|
| - js = {}
|
| fields = message.ListFields()
|
| include_default = including_default_value_fields
|
|
|
| @@ -200,6 +204,79 @@ def _FieldToJsonObject(
|
| return value
|
|
|
|
|
| +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 _ListValueMessageToJsonObject(message, unused_including_default=False):
|
| + """Converts ListValue message according to Proto3 JSON Specification."""
|
| + return [_ValueMessageToJsonObject(value)
|
| + for value in message.values]
|
| +
|
| +
|
| +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 _IsWrapperMessage(message_descriptor):
|
| return message_descriptor.file.name == 'google/protobuf/wrappers.proto'
|
|
|
| @@ -231,7 +308,7 @@ def Parse(text, message):
|
| Raises::
|
| ParseError: On JSON parsing problems.
|
| """
|
| - if not isinstance(text, text_type): text = text.decode('utf-8')
|
| + if not isinstance(text, six.text_type): text = text.decode('utf-8')
|
| try:
|
| if sys.version_info < (2, 7):
|
| # object_pair_hook is not supported before python2.7
|
| @@ -240,7 +317,7 @@ 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)))
|
| - _ConvertFieldValuePair(js, message)
|
| + _ConvertMessage(js, message)
|
| return message
|
|
|
|
|
| @@ -291,13 +368,22 @@ def _ConvertFieldValuePair(js, message):
|
| if not isinstance(value, list):
|
| raise ParseError('repeated field {0} must be in [] which is '
|
| '{1}.'.format(name, value))
|
| - for item in value:
|
| - if item is None:
|
| - continue
|
| - if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
| + 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:
|
| + 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:
|
| @@ -327,13 +413,87 @@ def _ConvertMessage(value, message):
|
| ParseError: In case of convert problems.
|
| """
|
| message_descriptor = message.DESCRIPTOR
|
| - if hasattr(message, 'FromJsonString'):
|
| - message.FromJsonString(value)
|
| - elif _IsWrapperMessage(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)
|
| +
|
| +
|
| +_INT_OR_FLOAT = six.integer_types + (float,)
|
| +
|
| +
|
| +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']
|
| @@ -353,7 +513,8 @@ def _ConvertMapFieldValue(value, message, field):
|
| """
|
| if not isinstance(value, dict):
|
| raise ParseError(
|
| - 'Map fieled {0} must be in {} which is {1}.'.format(field.name, value))
|
| + '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:
|
| @@ -416,7 +577,7 @@ def _ConvertInteger(value):
|
| if isinstance(value, float):
|
| raise ParseError('Couldn\'t parse integer: {0}.'.format(value))
|
|
|
| - if isinstance(value, text_type) and value.find(' ') != -1:
|
| + if isinstance(value, six.text_type) and value.find(' ') != -1:
|
| raise ParseError('Couldn\'t parse integer: "{0}".'.format(value))
|
|
|
| return int(value)
|
| @@ -465,3 +626,20 @@ def _ConvertBool(value, require_str):
|
| if not isinstance(value, bool):
|
| raise ParseError('Expected true or false without quotes.')
|
| 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]
|
| +}
|
|
|