| OLD | NEW |
| 1 # Protocol Buffers - Google's data interchange format | 1 # Protocol Buffers - Google's data interchange format |
| 2 # Copyright 2008 Google Inc. All rights reserved. | 2 # Copyright 2008 Google Inc. All rights reserved. |
| 3 # https://developers.google.com/protocol-buffers/ | 3 # https://developers.google.com/protocol-buffers/ |
| 4 # | 4 # |
| 5 # Redistribution and use in source and binary forms, with or without | 5 # Redistribution and use in source and binary forms, with or without |
| 6 # modification, are permitted provided that the following conditions are | 6 # modification, are permitted provided that the following conditions are |
| 7 # met: | 7 # met: |
| 8 # | 8 # |
| 9 # * Redistributions of source code must retain the above copyright | 9 # * Redistributions of source code must retain the above copyright |
| 10 # notice, this list of conditions and the following disclaimer. | 10 # notice, this list of conditions and the following disclaimer. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 # Create a proto object and serialize it to a json format string. | 35 # Create a proto object and serialize it to a json format string. |
| 36 message = my_proto_pb2.MyMessage(foo='bar') | 36 message = my_proto_pb2.MyMessage(foo='bar') |
| 37 json_string = json_format.MessageToJson(message) | 37 json_string = json_format.MessageToJson(message) |
| 38 | 38 |
| 39 # Parse a json format string to proto object. | 39 # Parse a json format string to proto object. |
| 40 message = json_format.Parse(json_string, my_proto_pb2.MyMessage()) | 40 message = json_format.Parse(json_string, my_proto_pb2.MyMessage()) |
| 41 """ | 41 """ |
| 42 | 42 |
| 43 __author__ = 'jieluo@google.com (Jie Luo)' | 43 __author__ = 'jieluo@google.com (Jie Luo)' |
| 44 | 44 |
| 45 try: |
| 46 from collections import OrderedDict |
| 47 except ImportError: |
| 48 from ordereddict import OrderedDict #PY26 |
| 45 import base64 | 49 import base64 |
| 46 import json | 50 import json |
| 47 import math | 51 import math |
| 52 import re |
| 48 import six | 53 import six |
| 49 import sys | 54 import sys |
| 50 | 55 |
| 56 from operator import methodcaller |
| 51 from google.protobuf import descriptor | 57 from google.protobuf import descriptor |
| 52 from google.protobuf import symbol_database | 58 from google.protobuf import symbol_database |
| 53 | 59 |
| 54 _TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S' | 60 _TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S' |
| 55 _INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32, | 61 _INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32, |
| 56 descriptor.FieldDescriptor.CPPTYPE_UINT32, | 62 descriptor.FieldDescriptor.CPPTYPE_UINT32, |
| 57 descriptor.FieldDescriptor.CPPTYPE_INT64, | 63 descriptor.FieldDescriptor.CPPTYPE_INT64, |
| 58 descriptor.FieldDescriptor.CPPTYPE_UINT64]) | 64 descriptor.FieldDescriptor.CPPTYPE_UINT64]) |
| 59 _INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64, | 65 _INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64, |
| 60 descriptor.FieldDescriptor.CPPTYPE_UINT64]) | 66 descriptor.FieldDescriptor.CPPTYPE_UINT64]) |
| 61 _FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT, | 67 _FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT, |
| 62 descriptor.FieldDescriptor.CPPTYPE_DOUBLE]) | 68 descriptor.FieldDescriptor.CPPTYPE_DOUBLE]) |
| 63 _INFINITY = 'Infinity' | 69 _INFINITY = 'Infinity' |
| 64 _NEG_INFINITY = '-Infinity' | 70 _NEG_INFINITY = '-Infinity' |
| 65 _NAN = 'NaN' | 71 _NAN = 'NaN' |
| 66 | 72 |
| 73 _UNPAIRED_SURROGATE_PATTERN = re.compile(six.u( |
| 74 r'[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]' |
| 75 )) |
| 67 | 76 |
| 68 class Error(Exception): | 77 class Error(Exception): |
| 69 """Top-level module error for json_format.""" | 78 """Top-level module error for json_format.""" |
| 70 | 79 |
| 71 | 80 |
| 72 class SerializeToJsonError(Error): | 81 class SerializeToJsonError(Error): |
| 73 """Thrown if serialization to JSON fails.""" | 82 """Thrown if serialization to JSON fails.""" |
| 74 | 83 |
| 75 | 84 |
| 76 class ParseError(Error): | 85 class ParseError(Error): |
| 77 """Thrown in case of parsing error.""" | 86 """Thrown in case of parsing error.""" |
| 78 | 87 |
| 79 | 88 |
| 80 def MessageToJson(message, including_default_value_fields=False): | 89 def MessageToJson(message, including_default_value_fields=False): |
| 81 """Converts protobuf message to JSON format. | 90 """Converts protobuf message to JSON format. |
| 82 | 91 |
| 83 Args: | 92 Args: |
| 84 message: The protocol buffers message instance to serialize. | 93 message: The protocol buffers message instance to serialize. |
| 85 including_default_value_fields: If True, singular primitive fields, | 94 including_default_value_fields: If True, singular primitive fields, |
| 86 repeated fields, and map fields will always be serialized. If | 95 repeated fields, and map fields will always be serialized. If |
| 87 False, only serialize non-empty fields. Singular message fields | 96 False, only serialize non-empty fields. Singular message fields |
| 88 and oneof fields are not affected by this option. | 97 and oneof fields are not affected by this option. |
| 89 | 98 |
| 90 Returns: | 99 Returns: |
| 91 A string containing the JSON formatted protocol buffer message. | 100 A string containing the JSON formatted protocol buffer message. |
| 92 """ | 101 """ |
| 93 js = _MessageToJsonObject(message, including_default_value_fields) | 102 printer = _Printer(including_default_value_fields) |
| 94 return json.dumps(js, indent=2) | 103 return printer.ToJsonString(message) |
| 95 | |
| 96 | |
| 97 def _MessageToJsonObject(message, including_default_value_fields): | |
| 98 """Converts message to an object according to Proto3 JSON Specification.""" | |
| 99 message_descriptor = message.DESCRIPTOR | |
| 100 full_name = message_descriptor.full_name | |
| 101 if _IsWrapperMessage(message_descriptor): | |
| 102 return _WrapperMessageToJsonObject(message) | |
| 103 if full_name in _WKTJSONMETHODS: | |
| 104 return _WKTJSONMETHODS[full_name][0]( | |
| 105 message, including_default_value_fields) | |
| 106 js = {} | |
| 107 return _RegularMessageToJsonObject( | |
| 108 message, js, including_default_value_fields) | |
| 109 | 104 |
| 110 | 105 |
| 111 def _IsMapEntry(field): | 106 def _IsMapEntry(field): |
| 112 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and | 107 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and |
| 113 field.message_type.has_options and | 108 field.message_type.has_options and |
| 114 field.message_type.GetOptions().map_entry) | 109 field.message_type.GetOptions().map_entry) |
| 115 | 110 |
| 116 | 111 |
| 117 def _RegularMessageToJsonObject(message, js, including_default_value_fields): | 112 class _Printer(object): |
| 118 """Converts normal message according to Proto3 JSON Specification.""" | 113 """JSON format printer for protocol message.""" |
| 119 fields = message.ListFields() | |
| 120 include_default = including_default_value_fields | |
| 121 | 114 |
| 122 try: | 115 def __init__(self, |
| 123 for field, value in fields: | 116 including_default_value_fields=False): |
| 124 name = field.camelcase_name | 117 self.including_default_value_fields = including_default_value_fields |
| 125 if _IsMapEntry(field): | 118 |
| 126 # Convert a map field. | 119 def ToJsonString(self, message): |
| 127 v_field = field.message_type.fields_by_name['value'] | 120 js = self._MessageToJsonObject(message) |
| 128 js_map = {} | 121 return json.dumps(js, indent=2) |
| 129 for key in value: | 122 |
| 130 if isinstance(key, bool): | 123 def _MessageToJsonObject(self, message): |
| 131 if key: | 124 """Converts message to an object according to Proto3 JSON Specification.""" |
| 132 recorded_key = 'true' | 125 message_descriptor = message.DESCRIPTOR |
| 126 full_name = message_descriptor.full_name |
| 127 if _IsWrapperMessage(message_descriptor): |
| 128 return self._WrapperMessageToJsonObject(message) |
| 129 if full_name in _WKTJSONMETHODS: |
| 130 return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self) |
| 131 js = {} |
| 132 return self._RegularMessageToJsonObject(message, js) |
| 133 |
| 134 def _RegularMessageToJsonObject(self, message, js): |
| 135 """Converts normal message according to Proto3 JSON Specification.""" |
| 136 fields = message.ListFields() |
| 137 |
| 138 try: |
| 139 for field, value in fields: |
| 140 name = field.camelcase_name |
| 141 if _IsMapEntry(field): |
| 142 # Convert a map field. |
| 143 v_field = field.message_type.fields_by_name['value'] |
| 144 js_map = {} |
| 145 for key in value: |
| 146 if isinstance(key, bool): |
| 147 if key: |
| 148 recorded_key = 'true' |
| 149 else: |
| 150 recorded_key = 'false' |
| 133 else: | 151 else: |
| 134 recorded_key = 'false' | 152 recorded_key = key |
| 153 js_map[recorded_key] = self._FieldToJsonObject( |
| 154 v_field, value[key]) |
| 155 js[name] = js_map |
| 156 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: |
| 157 # Convert a repeated field. |
| 158 js[name] = [self._FieldToJsonObject(field, k) |
| 159 for k in value] |
| 160 else: |
| 161 js[name] = self._FieldToJsonObject(field, value) |
| 162 |
| 163 # Serialize default value if including_default_value_fields is True. |
| 164 if self.including_default_value_fields: |
| 165 message_descriptor = message.DESCRIPTOR |
| 166 for field in message_descriptor.fields: |
| 167 # Singular message fields and oneof fields will not be affected. |
| 168 if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and |
| 169 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or |
| 170 field.containing_oneof): |
| 171 continue |
| 172 name = field.camelcase_name |
| 173 if name in js: |
| 174 # Skip the field which has been serailized already. |
| 175 continue |
| 176 if _IsMapEntry(field): |
| 177 js[name] = {} |
| 178 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: |
| 179 js[name] = [] |
| 135 else: | 180 else: |
| 136 recorded_key = key | 181 js[name] = self._FieldToJsonObject(field, field.default_value) |
| 137 js_map[recorded_key] = _FieldToJsonObject( | 182 |
| 138 v_field, value[key], including_default_value_fields) | 183 except ValueError as e: |
| 139 js[name] = js_map | 184 raise SerializeToJsonError( |
| 140 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: | 185 'Failed to serialize {0} field: {1}.'.format(field.name, e)) |
| 141 # Convert a repeated field. | 186 |
| 142 js[name] = [_FieldToJsonObject(field, k, include_default) | 187 return js |
| 143 for k in value] | 188 |
| 189 def _FieldToJsonObject(self, field, value): |
| 190 """Converts field value according to Proto3 JSON Specification.""" |
| 191 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: |
| 192 return self._MessageToJsonObject(value) |
| 193 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: |
| 194 enum_value = field.enum_type.values_by_number.get(value, None) |
| 195 if enum_value is not None: |
| 196 return enum_value.name |
| 144 else: | 197 else: |
| 145 js[name] = _FieldToJsonObject(field, value, include_default) | 198 raise SerializeToJsonError('Enum field contains an integer value ' |
| 199 'which can not mapped to an enum value.') |
| 200 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: |
| 201 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: |
| 202 # Use base64 Data encoding for bytes |
| 203 return base64.b64encode(value).decode('utf-8') |
| 204 else: |
| 205 return value |
| 206 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: |
| 207 return bool(value) |
| 208 elif field.cpp_type in _INT64_TYPES: |
| 209 return str(value) |
| 210 elif field.cpp_type in _FLOAT_TYPES: |
| 211 if math.isinf(value): |
| 212 if value < 0.0: |
| 213 return _NEG_INFINITY |
| 214 else: |
| 215 return _INFINITY |
| 216 if math.isnan(value): |
| 217 return _NAN |
| 218 return value |
| 146 | 219 |
| 147 # Serialize default value if including_default_value_fields is True. | 220 def _AnyMessageToJsonObject(self, message): |
| 148 if including_default_value_fields: | 221 """Converts Any message according to Proto3 JSON Specification.""" |
| 149 message_descriptor = message.DESCRIPTOR | 222 if not message.ListFields(): |
| 150 for field in message_descriptor.fields: | 223 return {} |
| 151 # Singular message fields and oneof fields will not be affected. | 224 # Must print @type first, use OrderedDict instead of {} |
| 152 if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and | 225 js = OrderedDict() |
| 153 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or | 226 type_url = message.type_url |
| 154 field.containing_oneof): | 227 js['@type'] = type_url |
| 155 continue | 228 sub_message = _CreateMessageFromTypeUrl(type_url) |
| 156 name = field.camelcase_name | 229 sub_message.ParseFromString(message.value) |
| 157 if name in js: | 230 message_descriptor = sub_message.DESCRIPTOR |
| 158 # Skip the field which has been serailized already. | 231 full_name = message_descriptor.full_name |
| 159 continue | 232 if _IsWrapperMessage(message_descriptor): |
| 160 if _IsMapEntry(field): | 233 js['value'] = self._WrapperMessageToJsonObject(sub_message) |
| 161 js[name] = {} | 234 return js |
| 162 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: | 235 if full_name in _WKTJSONMETHODS: |
| 163 js[name] = [] | 236 js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0], |
| 164 else: | 237 sub_message)(self) |
| 165 js[name] = _FieldToJsonObject(field, field.default_value) | 238 return js |
| 239 return self._RegularMessageToJsonObject(sub_message, js) |
| 166 | 240 |
| 167 except ValueError as e: | 241 def _GenericMessageToJsonObject(self, message): |
| 168 raise SerializeToJsonError( | 242 """Converts message according to Proto3 JSON Specification.""" |
| 169 'Failed to serialize {0} field: {1}.'.format(field.name, e)) | 243 # Duration, Timestamp and FieldMask have ToJsonString method to do the |
| 244 # convert. Users can also call the method directly. |
| 245 return message.ToJsonString() |
| 170 | 246 |
| 171 return js | 247 def _ValueMessageToJsonObject(self, message): |
| 248 """Converts Value message according to Proto3 JSON Specification.""" |
| 249 which = message.WhichOneof('kind') |
| 250 # If the Value message is not set treat as null_value when serialize |
| 251 # to JSON. The parse back result will be different from original message. |
| 252 if which is None or which == 'null_value': |
| 253 return None |
| 254 if which == 'list_value': |
| 255 return self._ListValueMessageToJsonObject(message.list_value) |
| 256 if which == 'struct_value': |
| 257 value = message.struct_value |
| 258 else: |
| 259 value = getattr(message, which) |
| 260 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] |
| 261 return self._FieldToJsonObject(oneof_descriptor, value) |
| 172 | 262 |
| 263 def _ListValueMessageToJsonObject(self, message): |
| 264 """Converts ListValue message according to Proto3 JSON Specification.""" |
| 265 return [self._ValueMessageToJsonObject(value) |
| 266 for value in message.values] |
| 173 | 267 |
| 174 def _FieldToJsonObject( | 268 def _StructMessageToJsonObject(self, message): |
| 175 field, value, including_default_value_fields=False): | 269 """Converts Struct message according to Proto3 JSON Specification.""" |
| 176 """Converts field value according to Proto3 JSON Specification.""" | 270 fields = message.fields |
| 177 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: | 271 ret = {} |
| 178 return _MessageToJsonObject(value, including_default_value_fields) | 272 for key in fields: |
| 179 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: | 273 ret[key] = self._ValueMessageToJsonObject(fields[key]) |
| 180 enum_value = field.enum_type.values_by_number.get(value, None) | 274 return ret |
| 181 if enum_value is not None: | |
| 182 return enum_value.name | |
| 183 else: | |
| 184 raise SerializeToJsonError('Enum field contains an integer value ' | |
| 185 'which can not mapped to an enum value.') | |
| 186 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: | |
| 187 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: | |
| 188 # Use base64 Data encoding for bytes | |
| 189 return base64.b64encode(value).decode('utf-8') | |
| 190 else: | |
| 191 return value | |
| 192 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: | |
| 193 return bool(value) | |
| 194 elif field.cpp_type in _INT64_TYPES: | |
| 195 return str(value) | |
| 196 elif field.cpp_type in _FLOAT_TYPES: | |
| 197 if math.isinf(value): | |
| 198 if value < 0.0: | |
| 199 return _NEG_INFINITY | |
| 200 else: | |
| 201 return _INFINITY | |
| 202 if math.isnan(value): | |
| 203 return _NAN | |
| 204 return value | |
| 205 | 275 |
| 206 | 276 def _WrapperMessageToJsonObject(self, message): |
| 207 def _AnyMessageToJsonObject(message, including_default): | 277 return self._FieldToJsonObject( |
| 208 """Converts Any message according to Proto3 JSON Specification.""" | 278 message.DESCRIPTOR.fields_by_name['value'], message.value) |
| 209 if not message.ListFields(): | |
| 210 return {} | |
| 211 js = {} | |
| 212 type_url = message.type_url | |
| 213 js['@type'] = type_url | |
| 214 sub_message = _CreateMessageFromTypeUrl(type_url) | |
| 215 sub_message.ParseFromString(message.value) | |
| 216 message_descriptor = sub_message.DESCRIPTOR | |
| 217 full_name = message_descriptor.full_name | |
| 218 if _IsWrapperMessage(message_descriptor): | |
| 219 js['value'] = _WrapperMessageToJsonObject(sub_message) | |
| 220 return js | |
| 221 if full_name in _WKTJSONMETHODS: | |
| 222 js['value'] = _WKTJSONMETHODS[full_name][0](sub_message, including_default) | |
| 223 return js | |
| 224 return _RegularMessageToJsonObject(sub_message, js, including_default) | |
| 225 | |
| 226 | |
| 227 def _CreateMessageFromTypeUrl(type_url): | |
| 228 # TODO(jieluo): Should add a way that users can register the type resolver | |
| 229 # instead of the default one. | |
| 230 db = symbol_database.Default() | |
| 231 type_name = type_url.split('/')[-1] | |
| 232 try: | |
| 233 message_descriptor = db.pool.FindMessageTypeByName(type_name) | |
| 234 except KeyError: | |
| 235 raise TypeError( | |
| 236 'Can not find message descriptor by type_url: {0}.'.format(type_url)) | |
| 237 message_class = db.GetPrototype(message_descriptor) | |
| 238 return message_class() | |
| 239 | |
| 240 | |
| 241 def _GenericMessageToJsonObject(message, unused_including_default): | |
| 242 """Converts message by ToJsonString according to Proto3 JSON Specification.""" | |
| 243 # Duration, Timestamp and FieldMask have ToJsonString method to do the | |
| 244 # convert. Users can also call the method directly. | |
| 245 return message.ToJsonString() | |
| 246 | |
| 247 | |
| 248 def _ValueMessageToJsonObject(message, unused_including_default=False): | |
| 249 """Converts Value message according to Proto3 JSON Specification.""" | |
| 250 which = message.WhichOneof('kind') | |
| 251 # If the Value message is not set treat as null_value when serialize | |
| 252 # to JSON. The parse back result will be different from original message. | |
| 253 if which is None or which == 'null_value': | |
| 254 return None | |
| 255 if which == 'list_value': | |
| 256 return _ListValueMessageToJsonObject(message.list_value) | |
| 257 if which == 'struct_value': | |
| 258 value = message.struct_value | |
| 259 else: | |
| 260 value = getattr(message, which) | |
| 261 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] | |
| 262 return _FieldToJsonObject(oneof_descriptor, value) | |
| 263 | |
| 264 | |
| 265 def _ListValueMessageToJsonObject(message, unused_including_default=False): | |
| 266 """Converts ListValue message according to Proto3 JSON Specification.""" | |
| 267 return [_ValueMessageToJsonObject(value) | |
| 268 for value in message.values] | |
| 269 | |
| 270 | |
| 271 def _StructMessageToJsonObject(message, unused_including_default=False): | |
| 272 """Converts Struct message according to Proto3 JSON Specification.""" | |
| 273 fields = message.fields | |
| 274 ret = {} | |
| 275 for key in fields: | |
| 276 ret[key] = _ValueMessageToJsonObject(fields[key]) | |
| 277 return ret | |
| 278 | 279 |
| 279 | 280 |
| 280 def _IsWrapperMessage(message_descriptor): | 281 def _IsWrapperMessage(message_descriptor): |
| 281 return message_descriptor.file.name == 'google/protobuf/wrappers.proto' | 282 return message_descriptor.file.name == 'google/protobuf/wrappers.proto' |
| 282 | 283 |
| 283 | 284 |
| 284 def _WrapperMessageToJsonObject(message): | |
| 285 return _FieldToJsonObject( | |
| 286 message.DESCRIPTOR.fields_by_name['value'], message.value) | |
| 287 | |
| 288 | |
| 289 def _DuplicateChecker(js): | 285 def _DuplicateChecker(js): |
| 290 result = {} | 286 result = {} |
| 291 for name, value in js: | 287 for name, value in js: |
| 292 if name in result: | 288 if name in result: |
| 293 raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name)) | 289 raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name)) |
| 294 result[name] = value | 290 result[name] = value |
| 295 return result | 291 return result |
| 296 | 292 |
| 297 | 293 |
| 298 def Parse(text, message): | 294 def _CreateMessageFromTypeUrl(type_url): |
| 295 # TODO(jieluo): Should add a way that users can register the type resolver |
| 296 # instead of the default one. |
| 297 db = symbol_database.Default() |
| 298 type_name = type_url.split('/')[-1] |
| 299 try: |
| 300 message_descriptor = db.pool.FindMessageTypeByName(type_name) |
| 301 except KeyError: |
| 302 raise TypeError( |
| 303 'Can not find message descriptor by type_url: {0}.'.format(type_url)) |
| 304 message_class = db.GetPrototype(message_descriptor) |
| 305 return message_class() |
| 306 |
| 307 |
| 308 def Parse(text, message, ignore_unknown_fields=False): |
| 299 """Parses a JSON representation of a protocol message into a message. | 309 """Parses a JSON representation of a protocol message into a message. |
| 300 | 310 |
| 301 Args: | 311 Args: |
| 302 text: Message JSON representation. | 312 text: Message JSON representation. |
| 303 message: A protocol beffer message to merge into. | 313 message: A protocol beffer message to merge into. |
| 314 ignore_unknown_fields: If True, do not raise errors for unknown fields. |
| 304 | 315 |
| 305 Returns: | 316 Returns: |
| 306 The same message passed as argument. | 317 The same message passed as argument. |
| 307 | 318 |
| 308 Raises:: | 319 Raises:: |
| 309 ParseError: On JSON parsing problems. | 320 ParseError: On JSON parsing problems. |
| 310 """ | 321 """ |
| 311 if not isinstance(text, six.text_type): text = text.decode('utf-8') | 322 if not isinstance(text, six.text_type): text = text.decode('utf-8') |
| 312 try: | 323 try: |
| 313 if sys.version_info < (2, 7): | 324 if sys.version_info < (2, 7): |
| 314 # object_pair_hook is not supported before python2.7 | 325 # object_pair_hook is not supported before python2.7 |
| 315 js = json.loads(text) | 326 js = json.loads(text) |
| 316 else: | 327 else: |
| 317 js = json.loads(text, object_pairs_hook=_DuplicateChecker) | 328 js = json.loads(text, object_pairs_hook=_DuplicateChecker) |
| 318 except ValueError as e: | 329 except ValueError as e: |
| 319 raise ParseError('Failed to load JSON: {0}.'.format(str(e))) | 330 raise ParseError('Failed to load JSON: {0}.'.format(str(e))) |
| 320 _ConvertMessage(js, message) | 331 parser = _Parser(ignore_unknown_fields) |
| 332 parser.ConvertMessage(js, message) |
| 321 return message | 333 return message |
| 322 | 334 |
| 323 | 335 |
| 324 def _ConvertFieldValuePair(js, message): | 336 _INT_OR_FLOAT = six.integer_types + (float,) |
| 325 """Convert field value pairs into regular message. | 337 |
| 326 | 338 |
| 327 Args: | 339 class _Parser(object): |
| 328 js: A JSON object to convert the field value pairs. | 340 """JSON format parser for protocol message.""" |
| 329 message: A regular protocol message to record the data. | 341 |
| 330 | 342 def __init__(self, |
| 331 Raises: | 343 ignore_unknown_fields): |
| 332 ParseError: In case of problems converting. | 344 self.ignore_unknown_fields = ignore_unknown_fields |
| 333 """ | 345 |
| 334 names = [] | 346 def ConvertMessage(self, value, message): |
| 335 message_descriptor = message.DESCRIPTOR | 347 """Convert a JSON object into a message. |
| 336 for name in js: | 348 |
| 349 Args: |
| 350 value: A JSON object. |
| 351 message: A WKT or regular protocol message to record the data. |
| 352 |
| 353 Raises: |
| 354 ParseError: In case of convert problems. |
| 355 """ |
| 356 message_descriptor = message.DESCRIPTOR |
| 357 full_name = message_descriptor.full_name |
| 358 if _IsWrapperMessage(message_descriptor): |
| 359 self._ConvertWrapperMessage(value, message) |
| 360 elif full_name in _WKTJSONMETHODS: |
| 361 methodcaller(_WKTJSONMETHODS[full_name][1], value, message)(self) |
| 362 else: |
| 363 self._ConvertFieldValuePair(value, message) |
| 364 |
| 365 def _ConvertFieldValuePair(self, js, message): |
| 366 """Convert field value pairs into regular message. |
| 367 |
| 368 Args: |
| 369 js: A JSON object to convert the field value pairs. |
| 370 message: A regular protocol message to record the data. |
| 371 |
| 372 Raises: |
| 373 ParseError: In case of problems converting. |
| 374 """ |
| 375 names = [] |
| 376 message_descriptor = message.DESCRIPTOR |
| 377 for name in js: |
| 378 try: |
| 379 field = message_descriptor.fields_by_camelcase_name.get(name, None) |
| 380 if not field: |
| 381 if self.ignore_unknown_fields: |
| 382 continue |
| 383 raise ParseError( |
| 384 'Message type "{0}" has no field named "{1}".'.format( |
| 385 message_descriptor.full_name, name)) |
| 386 if name in names: |
| 387 raise ParseError('Message type "{0}" should not have multiple ' |
| 388 '"{1}" fields.'.format( |
| 389 message.DESCRIPTOR.full_name, name)) |
| 390 names.append(name) |
| 391 # Check no other oneof field is parsed. |
| 392 if field.containing_oneof is not None: |
| 393 oneof_name = field.containing_oneof.name |
| 394 if oneof_name in names: |
| 395 raise ParseError('Message type "{0}" should not have multiple ' |
| 396 '"{1}" oneof fields.'.format( |
| 397 message.DESCRIPTOR.full_name, oneof_name)) |
| 398 names.append(oneof_name) |
| 399 |
| 400 value = js[name] |
| 401 if value is None: |
| 402 message.ClearField(field.name) |
| 403 continue |
| 404 |
| 405 # Parse field value. |
| 406 if _IsMapEntry(field): |
| 407 message.ClearField(field.name) |
| 408 self._ConvertMapFieldValue(value, message, field) |
| 409 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: |
| 410 message.ClearField(field.name) |
| 411 if not isinstance(value, list): |
| 412 raise ParseError('repeated field {0} must be in [] which is ' |
| 413 '{1}.'.format(name, value)) |
| 414 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: |
| 415 # Repeated message field. |
| 416 for item in value: |
| 417 sub_message = getattr(message, field.name).add() |
| 418 # None is a null_value in Value. |
| 419 if (item is None and |
| 420 sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): |
| 421 raise ParseError('null is not allowed to be used as an element' |
| 422 ' in a repeated field.') |
| 423 self.ConvertMessage(item, sub_message) |
| 424 else: |
| 425 # Repeated scalar field. |
| 426 for item in value: |
| 427 if item is None: |
| 428 raise ParseError('null is not allowed to be used as an element' |
| 429 ' in a repeated field.') |
| 430 getattr(message, field.name).append( |
| 431 _ConvertScalarFieldValue(item, field)) |
| 432 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: |
| 433 sub_message = getattr(message, field.name) |
| 434 self.ConvertMessage(value, sub_message) |
| 435 else: |
| 436 setattr(message, field.name, _ConvertScalarFieldValue(value, field)) |
| 437 except ParseError as e: |
| 438 if field and field.containing_oneof is None: |
| 439 raise ParseError('Failed to parse {0} field: {1}'.format(name, e)) |
| 440 else: |
| 441 raise ParseError(str(e)) |
| 442 except ValueError as e: |
| 443 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) |
| 444 except TypeError as e: |
| 445 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) |
| 446 |
| 447 def _ConvertAnyMessage(self, value, message): |
| 448 """Convert a JSON representation into Any message.""" |
| 449 if isinstance(value, dict) and not value: |
| 450 return |
| 337 try: | 451 try: |
| 338 field = message_descriptor.fields_by_camelcase_name.get(name, None) | 452 type_url = value['@type'] |
| 339 if not field: | 453 except KeyError: |
| 340 raise ParseError( | 454 raise ParseError('@type is missing when parsing any message.') |
| 341 'Message type "{0}" has no field named "{1}".'.format( | 455 |
| 342 message_descriptor.full_name, name)) | 456 sub_message = _CreateMessageFromTypeUrl(type_url) |
| 343 if name in names: | 457 message_descriptor = sub_message.DESCRIPTOR |
| 344 raise ParseError( | 458 full_name = message_descriptor.full_name |
| 345 'Message type "{0}" should not have multiple "{1}" fields.'.format( | 459 if _IsWrapperMessage(message_descriptor): |
| 346 message.DESCRIPTOR.full_name, name)) | 460 self._ConvertWrapperMessage(value['value'], sub_message) |
| 347 names.append(name) | 461 elif full_name in _WKTJSONMETHODS: |
| 348 # Check no other oneof field is parsed. | 462 methodcaller( |
| 349 if field.containing_oneof is not None: | 463 _WKTJSONMETHODS[full_name][1], value['value'], sub_message)(self) |
| 350 oneof_name = field.containing_oneof.name | 464 else: |
| 351 if oneof_name in names: | 465 del value['@type'] |
| 352 raise ParseError('Message type "{0}" should not have multiple "{1}" ' | 466 self._ConvertFieldValuePair(value, sub_message) |
| 353 'oneof fields.'.format( | 467 # Sets Any message |
| 354 message.DESCRIPTOR.full_name, oneof_name)) | 468 message.value = sub_message.SerializeToString() |
| 355 names.append(oneof_name) | 469 message.type_url = type_url |
| 356 | 470 |
| 357 value = js[name] | 471 def _ConvertGenericMessage(self, value, message): |
| 358 if value is None: | 472 """Convert a JSON representation into message with FromJsonString.""" |
| 359 message.ClearField(field.name) | 473 # Durantion, Timestamp, FieldMask have FromJsonString method to do the |
| 360 continue | 474 # convert. Users can also call the method directly. |
| 361 | 475 message.FromJsonString(value) |
| 362 # Parse field value. | 476 |
| 363 if _IsMapEntry(field): | 477 def _ConvertValueMessage(self, value, message): |
| 364 message.ClearField(field.name) | 478 """Convert a JSON representation into Value message.""" |
| 365 _ConvertMapFieldValue(value, message, field) | 479 if isinstance(value, dict): |
| 366 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: | 480 self._ConvertStructMessage(value, message.struct_value) |
| 367 message.ClearField(field.name) | 481 elif isinstance(value, list): |
| 368 if not isinstance(value, list): | 482 self. _ConvertListValueMessage(value, message.list_value) |
| 369 raise ParseError('repeated field {0} must be in [] which is ' | 483 elif value is None: |
| 370 '{1}.'.format(name, value)) | 484 message.null_value = 0 |
| 371 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: | 485 elif isinstance(value, bool): |
| 372 # Repeated message field. | 486 message.bool_value = value |
| 373 for item in value: | 487 elif isinstance(value, six.string_types): |
| 374 sub_message = getattr(message, field.name).add() | 488 message.string_value = value |
| 375 # None is a null_value in Value. | 489 elif isinstance(value, _INT_OR_FLOAT): |
| 376 if (item is None and | 490 message.number_value = value |
| 377 sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): | 491 else: |
| 378 raise ParseError('null is not allowed to be used as an element' | 492 raise ParseError('Unexpected type for Value message.') |
| 379 ' in a repeated field.') | 493 |
| 380 _ConvertMessage(item, sub_message) | 494 def _ConvertListValueMessage(self, value, message): |
| 381 else: | 495 """Convert a JSON representation into ListValue message.""" |
| 382 # Repeated scalar field. | 496 if not isinstance(value, list): |
| 383 for item in value: | 497 raise ParseError( |
| 384 if item is None: | 498 'ListValue must be in [] which is {0}.'.format(value)) |
| 385 raise ParseError('null is not allowed to be used as an element' | 499 message.ClearField('values') |
| 386 ' in a repeated field.') | 500 for item in value: |
| 387 getattr(message, field.name).append( | 501 self._ConvertValueMessage(item, message.values.add()) |
| 388 _ConvertScalarFieldValue(item, field)) | 502 |
| 389 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: | 503 def _ConvertStructMessage(self, value, message): |
| 390 sub_message = getattr(message, field.name) | 504 """Convert a JSON representation into Struct message.""" |
| 391 _ConvertMessage(value, sub_message) | 505 if not isinstance(value, dict): |
| 506 raise ParseError( |
| 507 'Struct must be in a dict which is {0}.'.format(value)) |
| 508 for key in value: |
| 509 self._ConvertValueMessage(value[key], message.fields[key]) |
| 510 return |
| 511 |
| 512 def _ConvertWrapperMessage(self, value, message): |
| 513 """Convert a JSON representation into Wrapper message.""" |
| 514 field = message.DESCRIPTOR.fields_by_name['value'] |
| 515 setattr(message, 'value', _ConvertScalarFieldValue(value, field)) |
| 516 |
| 517 def _ConvertMapFieldValue(self, value, message, field): |
| 518 """Convert map field value for a message map field. |
| 519 |
| 520 Args: |
| 521 value: A JSON object to convert the map field value. |
| 522 message: A protocol message to record the converted data. |
| 523 field: The descriptor of the map field to be converted. |
| 524 |
| 525 Raises: |
| 526 ParseError: In case of convert problems. |
| 527 """ |
| 528 if not isinstance(value, dict): |
| 529 raise ParseError( |
| 530 'Map field {0} must be in a dict which is {1}.'.format( |
| 531 field.name, value)) |
| 532 key_field = field.message_type.fields_by_name['key'] |
| 533 value_field = field.message_type.fields_by_name['value'] |
| 534 for key in value: |
| 535 key_value = _ConvertScalarFieldValue(key, key_field, True) |
| 536 if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: |
| 537 self.ConvertMessage(value[key], getattr( |
| 538 message, field.name)[key_value]) |
| 392 else: | 539 else: |
| 393 setattr(message, field.name, _ConvertScalarFieldValue(value, field)) | 540 getattr(message, field.name)[key_value] = _ConvertScalarFieldValue( |
| 394 except ParseError as e: | 541 value[key], value_field) |
| 395 if field and field.containing_oneof is None: | |
| 396 raise ParseError('Failed to parse {0} field: {1}'.format(name, e)) | |
| 397 else: | |
| 398 raise ParseError(str(e)) | |
| 399 except ValueError as e: | |
| 400 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) | |
| 401 except TypeError as e: | |
| 402 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) | |
| 403 | |
| 404 | |
| 405 def _ConvertMessage(value, message): | |
| 406 """Convert a JSON object into a message. | |
| 407 | |
| 408 Args: | |
| 409 value: A JSON object. | |
| 410 message: A WKT or regular protocol message to record the data. | |
| 411 | |
| 412 Raises: | |
| 413 ParseError: In case of convert problems. | |
| 414 """ | |
| 415 message_descriptor = message.DESCRIPTOR | |
| 416 full_name = message_descriptor.full_name | |
| 417 if _IsWrapperMessage(message_descriptor): | |
| 418 _ConvertWrapperMessage(value, message) | |
| 419 elif full_name in _WKTJSONMETHODS: | |
| 420 _WKTJSONMETHODS[full_name][1](value, message) | |
| 421 else: | |
| 422 _ConvertFieldValuePair(value, message) | |
| 423 | |
| 424 | |
| 425 def _ConvertAnyMessage(value, message): | |
| 426 """Convert a JSON representation into Any message.""" | |
| 427 if isinstance(value, dict) and not value: | |
| 428 return | |
| 429 try: | |
| 430 type_url = value['@type'] | |
| 431 except KeyError: | |
| 432 raise ParseError('@type is missing when parsing any message.') | |
| 433 | |
| 434 sub_message = _CreateMessageFromTypeUrl(type_url) | |
| 435 message_descriptor = sub_message.DESCRIPTOR | |
| 436 full_name = message_descriptor.full_name | |
| 437 if _IsWrapperMessage(message_descriptor): | |
| 438 _ConvertWrapperMessage(value['value'], sub_message) | |
| 439 elif full_name in _WKTJSONMETHODS: | |
| 440 _WKTJSONMETHODS[full_name][1](value['value'], sub_message) | |
| 441 else: | |
| 442 del value['@type'] | |
| 443 _ConvertFieldValuePair(value, sub_message) | |
| 444 # Sets Any message | |
| 445 message.value = sub_message.SerializeToString() | |
| 446 message.type_url = type_url | |
| 447 | |
| 448 | |
| 449 def _ConvertGenericMessage(value, message): | |
| 450 """Convert a JSON representation into message with FromJsonString.""" | |
| 451 # Durantion, Timestamp, FieldMask have FromJsonString method to do the | |
| 452 # convert. Users can also call the method directly. | |
| 453 message.FromJsonString(value) | |
| 454 | |
| 455 | |
| 456 _INT_OR_FLOAT = six.integer_types + (float,) | |
| 457 | |
| 458 | |
| 459 def _ConvertValueMessage(value, message): | |
| 460 """Convert a JSON representation into Value message.""" | |
| 461 if isinstance(value, dict): | |
| 462 _ConvertStructMessage(value, message.struct_value) | |
| 463 elif isinstance(value, list): | |
| 464 _ConvertListValueMessage(value, message.list_value) | |
| 465 elif value is None: | |
| 466 message.null_value = 0 | |
| 467 elif isinstance(value, bool): | |
| 468 message.bool_value = value | |
| 469 elif isinstance(value, six.string_types): | |
| 470 message.string_value = value | |
| 471 elif isinstance(value, _INT_OR_FLOAT): | |
| 472 message.number_value = value | |
| 473 else: | |
| 474 raise ParseError('Unexpected type for Value message.') | |
| 475 | |
| 476 | |
| 477 def _ConvertListValueMessage(value, message): | |
| 478 """Convert a JSON representation into ListValue message.""" | |
| 479 if not isinstance(value, list): | |
| 480 raise ParseError( | |
| 481 'ListValue must be in [] which is {0}.'.format(value)) | |
| 482 message.ClearField('values') | |
| 483 for item in value: | |
| 484 _ConvertValueMessage(item, message.values.add()) | |
| 485 | |
| 486 | |
| 487 def _ConvertStructMessage(value, message): | |
| 488 """Convert a JSON representation into Struct message.""" | |
| 489 if not isinstance(value, dict): | |
| 490 raise ParseError( | |
| 491 'Struct must be in a dict which is {0}.'.format(value)) | |
| 492 for key in value: | |
| 493 _ConvertValueMessage(value[key], message.fields[key]) | |
| 494 return | |
| 495 | |
| 496 | |
| 497 def _ConvertWrapperMessage(value, message): | |
| 498 """Convert a JSON representation into Wrapper message.""" | |
| 499 field = message.DESCRIPTOR.fields_by_name['value'] | |
| 500 setattr(message, 'value', _ConvertScalarFieldValue(value, field)) | |
| 501 | |
| 502 | |
| 503 def _ConvertMapFieldValue(value, message, field): | |
| 504 """Convert map field value for a message map field. | |
| 505 | |
| 506 Args: | |
| 507 value: A JSON object to convert the map field value. | |
| 508 message: A protocol message to record the converted data. | |
| 509 field: The descriptor of the map field to be converted. | |
| 510 | |
| 511 Raises: | |
| 512 ParseError: In case of convert problems. | |
| 513 """ | |
| 514 if not isinstance(value, dict): | |
| 515 raise ParseError( | |
| 516 'Map field {0} must be in a dict which is {1}.'.format( | |
| 517 field.name, value)) | |
| 518 key_field = field.message_type.fields_by_name['key'] | |
| 519 value_field = field.message_type.fields_by_name['value'] | |
| 520 for key in value: | |
| 521 key_value = _ConvertScalarFieldValue(key, key_field, True) | |
| 522 if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: | |
| 523 _ConvertMessage(value[key], getattr(message, field.name)[key_value]) | |
| 524 else: | |
| 525 getattr(message, field.name)[key_value] = _ConvertScalarFieldValue( | |
| 526 value[key], value_field) | |
| 527 | 542 |
| 528 | 543 |
| 529 def _ConvertScalarFieldValue(value, field, require_str=False): | 544 def _ConvertScalarFieldValue(value, field, require_str=False): |
| 530 """Convert a single scalar field value. | 545 """Convert a single scalar field value. |
| 531 | 546 |
| 532 Args: | 547 Args: |
| 533 value: A scalar value to convert the scalar field value. | 548 value: A scalar value to convert the scalar field value. |
| 534 field: The descriptor of the field to convert. | 549 field: The descriptor of the field to convert. |
| 535 require_str: If True, the field value must be a str. | 550 require_str: If True, the field value must be a str. |
| 536 | 551 |
| 537 Returns: | 552 Returns: |
| 538 The converted scalar field value | 553 The converted scalar field value |
| 539 | 554 |
| 540 Raises: | 555 Raises: |
| 541 ParseError: In case of convert problems. | 556 ParseError: In case of convert problems. |
| 542 """ | 557 """ |
| 543 if field.cpp_type in _INT_TYPES: | 558 if field.cpp_type in _INT_TYPES: |
| 544 return _ConvertInteger(value) | 559 return _ConvertInteger(value) |
| 545 elif field.cpp_type in _FLOAT_TYPES: | 560 elif field.cpp_type in _FLOAT_TYPES: |
| 546 return _ConvertFloat(value) | 561 return _ConvertFloat(value) |
| 547 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: | 562 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: |
| 548 return _ConvertBool(value, require_str) | 563 return _ConvertBool(value, require_str) |
| 549 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: | 564 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: |
| 550 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: | 565 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: |
| 551 return base64.b64decode(value) | 566 return base64.b64decode(value) |
| 552 else: | 567 else: |
| 568 # Checking for unpaired surrogates appears to be unreliable, |
| 569 # depending on the specific Python version, so we check manually. |
| 570 if _UNPAIRED_SURROGATE_PATTERN.search(value): |
| 571 raise ParseError('Unpaired surrogate') |
| 553 return value | 572 return value |
| 554 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: | 573 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: |
| 555 # Convert an enum value. | 574 # Convert an enum value. |
| 556 enum_value = field.enum_type.values_by_name.get(value, None) | 575 enum_value = field.enum_type.values_by_name.get(value, None) |
| 557 if enum_value is None: | 576 if enum_value is None: |
| 558 raise ParseError( | 577 raise ParseError( |
| 559 'Enum value must be a string literal with double quotes. ' | 578 'Enum value must be a string literal with double quotes. ' |
| 560 'Type "{0}" has no value named {1}.'.format( | 579 'Type "{0}" has no value named {1}.'.format( |
| 561 field.enum_type.full_name, value)) | 580 field.enum_type.full_name, value)) |
| 562 return enum_value.number | 581 return enum_value.number |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 621 elif value == 'false': | 640 elif value == 'false': |
| 622 return False | 641 return False |
| 623 else: | 642 else: |
| 624 raise ParseError('Expected "true" or "false", not {0}.'.format(value)) | 643 raise ParseError('Expected "true" or "false", not {0}.'.format(value)) |
| 625 | 644 |
| 626 if not isinstance(value, bool): | 645 if not isinstance(value, bool): |
| 627 raise ParseError('Expected true or false without quotes.') | 646 raise ParseError('Expected true or false without quotes.') |
| 628 return value | 647 return value |
| 629 | 648 |
| 630 _WKTJSONMETHODS = { | 649 _WKTJSONMETHODS = { |
| 631 'google.protobuf.Any': [_AnyMessageToJsonObject, | 650 'google.protobuf.Any': ['_AnyMessageToJsonObject', |
| 632 _ConvertAnyMessage], | 651 '_ConvertAnyMessage'], |
| 633 'google.protobuf.Duration': [_GenericMessageToJsonObject, | 652 'google.protobuf.Duration': ['_GenericMessageToJsonObject', |
| 634 _ConvertGenericMessage], | 653 '_ConvertGenericMessage'], |
| 635 'google.protobuf.FieldMask': [_GenericMessageToJsonObject, | 654 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject', |
| 636 _ConvertGenericMessage], | 655 '_ConvertGenericMessage'], |
| 637 'google.protobuf.ListValue': [_ListValueMessageToJsonObject, | 656 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject', |
| 638 _ConvertListValueMessage], | 657 '_ConvertListValueMessage'], |
| 639 'google.protobuf.Struct': [_StructMessageToJsonObject, | 658 'google.protobuf.Struct': ['_StructMessageToJsonObject', |
| 640 _ConvertStructMessage], | 659 '_ConvertStructMessage'], |
| 641 'google.protobuf.Timestamp': [_GenericMessageToJsonObject, | 660 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject', |
| 642 _ConvertGenericMessage], | 661 '_ConvertGenericMessage'], |
| 643 'google.protobuf.Value': [_ValueMessageToJsonObject, | 662 'google.protobuf.Value': ['_ValueMessageToJsonObject', |
| 644 _ConvertValueMessage] | 663 '_ConvertValueMessage'] |
| 645 } | 664 } |
| OLD | NEW |