Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: third_party/protobuf/python/google/protobuf/json_format.py

Issue 2495533002: third_party/protobuf: Update to HEAD (83d681ee2c) (Closed)
Patch Set: Make chrome settings proto generated file a component Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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,
90 including_default_value_fields=False,
91 preserving_proto_field_name=False):
81 """Converts protobuf message to JSON format. 92 """Converts protobuf message to JSON format.
82 93
83 Args: 94 Args:
84 message: The protocol buffers message instance to serialize. 95 message: The protocol buffers message instance to serialize.
85 including_default_value_fields: If True, singular primitive fields, 96 including_default_value_fields: If True, singular primitive fields,
86 repeated fields, and map fields will always be serialized. If 97 repeated fields, and map fields will always be serialized. If
87 False, only serialize non-empty fields. Singular message fields 98 False, only serialize non-empty fields. Singular message fields
88 and oneof fields are not affected by this option. 99 and oneof fields are not affected by this option.
100 preserving_proto_field_name: If True, use the original proto field
101 names as defined in the .proto file. If False, convert the field
102 names to lowerCamelCase.
89 103
90 Returns: 104 Returns:
91 A string containing the JSON formatted protocol buffer message. 105 A string containing the JSON formatted protocol buffer message.
92 """ 106 """
93 js = _MessageToJsonObject(message, including_default_value_fields) 107 printer = _Printer(including_default_value_fields,
94 return json.dumps(js, indent=2) 108 preserving_proto_field_name)
109 return printer.ToJsonString(message)
95 110
96 111
97 def _MessageToJsonObject(message, including_default_value_fields): 112 def MessageToDict(message,
98 """Converts message to an object according to Proto3 JSON Specification.""" 113 including_default_value_fields=False,
99 message_descriptor = message.DESCRIPTOR 114 preserving_proto_field_name=False):
100 full_name = message_descriptor.full_name 115 """Converts protobuf message to a JSON dictionary.
101 if _IsWrapperMessage(message_descriptor): 116
102 return _WrapperMessageToJsonObject(message) 117 Args:
103 if full_name in _WKTJSONMETHODS: 118 message: The protocol buffers message instance to serialize.
104 return _WKTJSONMETHODS[full_name][0]( 119 including_default_value_fields: If True, singular primitive fields,
105 message, including_default_value_fields) 120 repeated fields, and map fields will always be serialized. If
106 js = {} 121 False, only serialize non-empty fields. Singular message fields
107 return _RegularMessageToJsonObject( 122 and oneof fields are not affected by this option.
108 message, js, including_default_value_fields) 123 preserving_proto_field_name: If True, use the original proto field
124 names as defined in the .proto file. If False, convert the field
125 names to lowerCamelCase.
126
127 Returns:
128 A dict representation of the JSON formatted protocol buffer message.
129 """
130 printer = _Printer(including_default_value_fields,
131 preserving_proto_field_name)
132 # pylint: disable=protected-access
133 return printer._MessageToJsonObject(message)
109 134
110 135
111 def _IsMapEntry(field): 136 def _IsMapEntry(field):
112 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and 137 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
113 field.message_type.has_options and 138 field.message_type.has_options and
114 field.message_type.GetOptions().map_entry) 139 field.message_type.GetOptions().map_entry)
115 140
116 141
117 def _RegularMessageToJsonObject(message, js, including_default_value_fields): 142 class _Printer(object):
118 """Converts normal message according to Proto3 JSON Specification.""" 143 """JSON format printer for protocol message."""
119 fields = message.ListFields()
120 include_default = including_default_value_fields
121 144
122 try: 145 def __init__(self,
123 for field, value in fields: 146 including_default_value_fields=False,
124 name = field.camelcase_name 147 preserving_proto_field_name=False):
125 if _IsMapEntry(field): 148 self.including_default_value_fields = including_default_value_fields
126 # Convert a map field. 149 self.preserving_proto_field_name = preserving_proto_field_name
127 v_field = field.message_type.fields_by_name['value'] 150
128 js_map = {} 151 def ToJsonString(self, message):
129 for key in value: 152 js = self._MessageToJsonObject(message)
130 if isinstance(key, bool): 153 return json.dumps(js, indent=2)
131 if key: 154
132 recorded_key = 'true' 155 def _MessageToJsonObject(self, message):
156 """Converts message to an object according to Proto3 JSON Specification."""
157 message_descriptor = message.DESCRIPTOR
158 full_name = message_descriptor.full_name
159 if _IsWrapperMessage(message_descriptor):
160 return self._WrapperMessageToJsonObject(message)
161 if full_name in _WKTJSONMETHODS:
162 return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self)
163 js = {}
164 return self._RegularMessageToJsonObject(message, js)
165
166 def _RegularMessageToJsonObject(self, message, js):
167 """Converts normal message according to Proto3 JSON Specification."""
168 fields = message.ListFields()
169
170 try:
171 for field, value in fields:
172 if self.preserving_proto_field_name:
173 name = field.name
174 else:
175 name = field.json_name
176 if _IsMapEntry(field):
177 # Convert a map field.
178 v_field = field.message_type.fields_by_name['value']
179 js_map = {}
180 for key in value:
181 if isinstance(key, bool):
182 if key:
183 recorded_key = 'true'
184 else:
185 recorded_key = 'false'
133 else: 186 else:
134 recorded_key = 'false' 187 recorded_key = key
188 js_map[recorded_key] = self._FieldToJsonObject(
189 v_field, value[key])
190 js[name] = js_map
191 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
192 # Convert a repeated field.
193 js[name] = [self._FieldToJsonObject(field, k)
194 for k in value]
195 else:
196 js[name] = self._FieldToJsonObject(field, value)
197
198 # Serialize default value if including_default_value_fields is True.
199 if self.including_default_value_fields:
200 message_descriptor = message.DESCRIPTOR
201 for field in message_descriptor.fields:
202 # Singular message fields and oneof fields will not be affected.
203 if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and
204 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or
205 field.containing_oneof):
206 continue
207 if self.preserving_proto_field_name:
208 name = field.name
135 else: 209 else:
136 recorded_key = key 210 name = field.json_name
137 js_map[recorded_key] = _FieldToJsonObject( 211 if name in js:
138 v_field, value[key], including_default_value_fields) 212 # Skip the field which has been serailized already.
139 js[name] = js_map 213 continue
140 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 214 if _IsMapEntry(field):
141 # Convert a repeated field. 215 js[name] = {}
142 js[name] = [_FieldToJsonObject(field, k, include_default) 216 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
143 for k in value] 217 js[name] = []
218 else:
219 js[name] = self._FieldToJsonObject(field, field.default_value)
220
221 except ValueError as e:
222 raise SerializeToJsonError(
223 'Failed to serialize {0} field: {1}.'.format(field.name, e))
224
225 return js
226
227 def _FieldToJsonObject(self, field, value):
228 """Converts field value according to Proto3 JSON Specification."""
229 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
230 return self._MessageToJsonObject(value)
231 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
232 enum_value = field.enum_type.values_by_number.get(value, None)
233 if enum_value is not None:
234 return enum_value.name
144 else: 235 else:
145 js[name] = _FieldToJsonObject(field, value, include_default) 236 raise SerializeToJsonError('Enum field contains an integer value '
237 'which can not mapped to an enum value.')
238 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
239 if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
240 # Use base64 Data encoding for bytes
241 return base64.b64encode(value).decode('utf-8')
242 else:
243 return value
244 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
245 return bool(value)
246 elif field.cpp_type in _INT64_TYPES:
247 return str(value)
248 elif field.cpp_type in _FLOAT_TYPES:
249 if math.isinf(value):
250 if value < 0.0:
251 return _NEG_INFINITY
252 else:
253 return _INFINITY
254 if math.isnan(value):
255 return _NAN
256 return value
146 257
147 # Serialize default value if including_default_value_fields is True. 258 def _AnyMessageToJsonObject(self, message):
148 if including_default_value_fields: 259 """Converts Any message according to Proto3 JSON Specification."""
149 message_descriptor = message.DESCRIPTOR 260 if not message.ListFields():
150 for field in message_descriptor.fields: 261 return {}
151 # Singular message fields and oneof fields will not be affected. 262 # Must print @type first, use OrderedDict instead of {}
152 if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and 263 js = OrderedDict()
153 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or 264 type_url = message.type_url
154 field.containing_oneof): 265 js['@type'] = type_url
155 continue 266 sub_message = _CreateMessageFromTypeUrl(type_url)
156 name = field.camelcase_name 267 sub_message.ParseFromString(message.value)
157 if name in js: 268 message_descriptor = sub_message.DESCRIPTOR
158 # Skip the field which has been serailized already. 269 full_name = message_descriptor.full_name
159 continue 270 if _IsWrapperMessage(message_descriptor):
160 if _IsMapEntry(field): 271 js['value'] = self._WrapperMessageToJsonObject(sub_message)
161 js[name] = {} 272 return js
162 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 273 if full_name in _WKTJSONMETHODS:
163 js[name] = [] 274 js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0],
164 else: 275 sub_message)(self)
165 js[name] = _FieldToJsonObject(field, field.default_value) 276 return js
277 return self._RegularMessageToJsonObject(sub_message, js)
166 278
167 except ValueError as e: 279 def _GenericMessageToJsonObject(self, message):
168 raise SerializeToJsonError( 280 """Converts message according to Proto3 JSON Specification."""
169 'Failed to serialize {0} field: {1}.'.format(field.name, e)) 281 # Duration, Timestamp and FieldMask have ToJsonString method to do the
282 # convert. Users can also call the method directly.
283 return message.ToJsonString()
170 284
171 return js 285 def _ValueMessageToJsonObject(self, message):
286 """Converts Value message according to Proto3 JSON Specification."""
287 which = message.WhichOneof('kind')
288 # If the Value message is not set treat as null_value when serialize
289 # to JSON. The parse back result will be different from original message.
290 if which is None or which == 'null_value':
291 return None
292 if which == 'list_value':
293 return self._ListValueMessageToJsonObject(message.list_value)
294 if which == 'struct_value':
295 value = message.struct_value
296 else:
297 value = getattr(message, which)
298 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]
299 return self._FieldToJsonObject(oneof_descriptor, value)
172 300
301 def _ListValueMessageToJsonObject(self, message):
302 """Converts ListValue message according to Proto3 JSON Specification."""
303 return [self._ValueMessageToJsonObject(value)
304 for value in message.values]
173 305
174 def _FieldToJsonObject( 306 def _StructMessageToJsonObject(self, message):
175 field, value, including_default_value_fields=False): 307 """Converts Struct message according to Proto3 JSON Specification."""
176 """Converts field value according to Proto3 JSON Specification.""" 308 fields = message.fields
177 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 309 ret = {}
178 return _MessageToJsonObject(value, including_default_value_fields) 310 for key in fields:
179 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 311 ret[key] = self._ValueMessageToJsonObject(fields[key])
180 enum_value = field.enum_type.values_by_number.get(value, None) 312 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 313
206 314 def _WrapperMessageToJsonObject(self, message):
207 def _AnyMessageToJsonObject(message, including_default): 315 return self._FieldToJsonObject(
208 """Converts Any message according to Proto3 JSON Specification.""" 316 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 317
279 318
280 def _IsWrapperMessage(message_descriptor): 319 def _IsWrapperMessage(message_descriptor):
281 return message_descriptor.file.name == 'google/protobuf/wrappers.proto' 320 return message_descriptor.file.name == 'google/protobuf/wrappers.proto'
282 321
283 322
284 def _WrapperMessageToJsonObject(message):
285 return _FieldToJsonObject(
286 message.DESCRIPTOR.fields_by_name['value'], message.value)
287
288
289 def _DuplicateChecker(js): 323 def _DuplicateChecker(js):
290 result = {} 324 result = {}
291 for name, value in js: 325 for name, value in js:
292 if name in result: 326 if name in result:
293 raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name)) 327 raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name))
294 result[name] = value 328 result[name] = value
295 return result 329 return result
296 330
297 331
298 def Parse(text, message): 332 def _CreateMessageFromTypeUrl(type_url):
333 # TODO(jieluo): Should add a way that users can register the type resolver
334 # instead of the default one.
335 db = symbol_database.Default()
336 type_name = type_url.split('/')[-1]
337 try:
338 message_descriptor = db.pool.FindMessageTypeByName(type_name)
339 except KeyError:
340 raise TypeError(
341 'Can not find message descriptor by type_url: {0}.'.format(type_url))
342 message_class = db.GetPrototype(message_descriptor)
343 return message_class()
344
345
346 def Parse(text, message, ignore_unknown_fields=False):
299 """Parses a JSON representation of a protocol message into a message. 347 """Parses a JSON representation of a protocol message into a message.
300 348
301 Args: 349 Args:
302 text: Message JSON representation. 350 text: Message JSON representation.
303 message: A protocol beffer message to merge into. 351 message: A protocol buffer message to merge into.
352 ignore_unknown_fields: If True, do not raise errors for unknown fields.
304 353
305 Returns: 354 Returns:
306 The same message passed as argument. 355 The same message passed as argument.
307 356
308 Raises:: 357 Raises::
309 ParseError: On JSON parsing problems. 358 ParseError: On JSON parsing problems.
310 """ 359 """
311 if not isinstance(text, six.text_type): text = text.decode('utf-8') 360 if not isinstance(text, six.text_type): text = text.decode('utf-8')
312 try: 361 try:
313 if sys.version_info < (2, 7): 362 if sys.version_info < (2, 7):
314 # object_pair_hook is not supported before python2.7 363 # object_pair_hook is not supported before python2.7
315 js = json.loads(text) 364 js = json.loads(text)
316 else: 365 else:
317 js = json.loads(text, object_pairs_hook=_DuplicateChecker) 366 js = json.loads(text, object_pairs_hook=_DuplicateChecker)
318 except ValueError as e: 367 except ValueError as e:
319 raise ParseError('Failed to load JSON: {0}.'.format(str(e))) 368 raise ParseError('Failed to load JSON: {0}.'.format(str(e)))
320 _ConvertMessage(js, message) 369 return ParseDict(js, message, ignore_unknown_fields)
370
371
372 def ParseDict(js_dict, message, ignore_unknown_fields=False):
373 """Parses a JSON dictionary representation into a message.
374
375 Args:
376 js_dict: Dict representation of a JSON message.
377 message: A protocol buffer message to merge into.
378 ignore_unknown_fields: If True, do not raise errors for unknown fields.
379
380 Returns:
381 The same message passed as argument.
382 """
383 parser = _Parser(ignore_unknown_fields)
384 parser.ConvertMessage(js_dict, message)
321 return message 385 return message
322 386
323 387
324 def _ConvertFieldValuePair(js, message): 388 _INT_OR_FLOAT = six.integer_types + (float,)
325 """Convert field value pairs into regular message. 389
326 390
327 Args: 391 class _Parser(object):
328 js: A JSON object to convert the field value pairs. 392 """JSON format parser for protocol message."""
329 message: A regular protocol message to record the data. 393
330 394 def __init__(self,
331 Raises: 395 ignore_unknown_fields):
332 ParseError: In case of problems converting. 396 self.ignore_unknown_fields = ignore_unknown_fields
333 """ 397
334 names = [] 398 def ConvertMessage(self, value, message):
335 message_descriptor = message.DESCRIPTOR 399 """Convert a JSON object into a message.
336 for name in js: 400
401 Args:
402 value: A JSON object.
403 message: A WKT or regular protocol message to record the data.
404
405 Raises:
406 ParseError: In case of convert problems.
407 """
408 message_descriptor = message.DESCRIPTOR
409 full_name = message_descriptor.full_name
410 if _IsWrapperMessage(message_descriptor):
411 self._ConvertWrapperMessage(value, message)
412 elif full_name in _WKTJSONMETHODS:
413 methodcaller(_WKTJSONMETHODS[full_name][1], value, message)(self)
414 else:
415 self._ConvertFieldValuePair(value, message)
416
417 def _ConvertFieldValuePair(self, js, message):
418 """Convert field value pairs into regular message.
419
420 Args:
421 js: A JSON object to convert the field value pairs.
422 message: A regular protocol message to record the data.
423
424 Raises:
425 ParseError: In case of problems converting.
426 """
427 names = []
428 message_descriptor = message.DESCRIPTOR
429 fields_by_json_name = dict((f.json_name, f)
430 for f in message_descriptor.fields)
431 for name in js:
432 try:
433 field = fields_by_json_name.get(name, None)
434 if not field:
435 field = message_descriptor.fields_by_name.get(name, None)
436 if not field:
437 if self.ignore_unknown_fields:
438 continue
439 raise ParseError(
440 'Message type "{0}" has no field named "{1}".'.format(
441 message_descriptor.full_name, name))
442 if name in names:
443 raise ParseError('Message type "{0}" should not have multiple '
444 '"{1}" fields.'.format(
445 message.DESCRIPTOR.full_name, name))
446 names.append(name)
447 # Check no other oneof field is parsed.
448 if field.containing_oneof is not None:
449 oneof_name = field.containing_oneof.name
450 if oneof_name in names:
451 raise ParseError('Message type "{0}" should not have multiple '
452 '"{1}" oneof fields.'.format(
453 message.DESCRIPTOR.full_name, oneof_name))
454 names.append(oneof_name)
455
456 value = js[name]
457 if value is None:
458 if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE
459 and field.message_type.full_name == 'google.protobuf.Value'):
460 sub_message = getattr(message, field.name)
461 sub_message.null_value = 0
462 else:
463 message.ClearField(field.name)
464 continue
465
466 # Parse field value.
467 if _IsMapEntry(field):
468 message.ClearField(field.name)
469 self._ConvertMapFieldValue(value, message, field)
470 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
471 message.ClearField(field.name)
472 if not isinstance(value, list):
473 raise ParseError('repeated field {0} must be in [] which is '
474 '{1}.'.format(name, value))
475 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
476 # Repeated message field.
477 for item in value:
478 sub_message = getattr(message, field.name).add()
479 # None is a null_value in Value.
480 if (item is None and
481 sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'):
482 raise ParseError('null is not allowed to be used as an element'
483 ' in a repeated field.')
484 self.ConvertMessage(item, sub_message)
485 else:
486 # Repeated scalar field.
487 for item in value:
488 if item is None:
489 raise ParseError('null is not allowed to be used as an element'
490 ' in a repeated field.')
491 getattr(message, field.name).append(
492 _ConvertScalarFieldValue(item, field))
493 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
494 sub_message = getattr(message, field.name)
495 sub_message.SetInParent()
496 self.ConvertMessage(value, sub_message)
497 else:
498 setattr(message, field.name, _ConvertScalarFieldValue(value, field))
499 except ParseError as e:
500 if field and field.containing_oneof is None:
501 raise ParseError('Failed to parse {0} field: {1}'.format(name, e))
502 else:
503 raise ParseError(str(e))
504 except ValueError as e:
505 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
506 except TypeError as e:
507 raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
508
509 def _ConvertAnyMessage(self, value, message):
510 """Convert a JSON representation into Any message."""
511 if isinstance(value, dict) and not value:
512 return
337 try: 513 try:
338 field = message_descriptor.fields_by_camelcase_name.get(name, None) 514 type_url = value['@type']
339 if not field: 515 except KeyError:
340 raise ParseError( 516 raise ParseError('@type is missing when parsing any message.')
341 'Message type "{0}" has no field named "{1}".'.format( 517
342 message_descriptor.full_name, name)) 518 sub_message = _CreateMessageFromTypeUrl(type_url)
343 if name in names: 519 message_descriptor = sub_message.DESCRIPTOR
344 raise ParseError( 520 full_name = message_descriptor.full_name
345 'Message type "{0}" should not have multiple "{1}" fields.'.format( 521 if _IsWrapperMessage(message_descriptor):
346 message.DESCRIPTOR.full_name, name)) 522 self._ConvertWrapperMessage(value['value'], sub_message)
347 names.append(name) 523 elif full_name in _WKTJSONMETHODS:
348 # Check no other oneof field is parsed. 524 methodcaller(
349 if field.containing_oneof is not None: 525 _WKTJSONMETHODS[full_name][1], value['value'], sub_message)(self)
350 oneof_name = field.containing_oneof.name 526 else:
351 if oneof_name in names: 527 del value['@type']
352 raise ParseError('Message type "{0}" should not have multiple "{1}" ' 528 self._ConvertFieldValuePair(value, sub_message)
353 'oneof fields.'.format( 529 # Sets Any message
354 message.DESCRIPTOR.full_name, oneof_name)) 530 message.value = sub_message.SerializeToString()
355 names.append(oneof_name) 531 message.type_url = type_url
356 532
357 value = js[name] 533 def _ConvertGenericMessage(self, value, message):
358 if value is None: 534 """Convert a JSON representation into message with FromJsonString."""
359 message.ClearField(field.name) 535 # Durantion, Timestamp, FieldMask have FromJsonString method to do the
360 continue 536 # convert. Users can also call the method directly.
361 537 message.FromJsonString(value)
362 # Parse field value. 538
363 if _IsMapEntry(field): 539 def _ConvertValueMessage(self, value, message):
364 message.ClearField(field.name) 540 """Convert a JSON representation into Value message."""
365 _ConvertMapFieldValue(value, message, field) 541 if isinstance(value, dict):
366 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 542 self._ConvertStructMessage(value, message.struct_value)
367 message.ClearField(field.name) 543 elif isinstance(value, list):
368 if not isinstance(value, list): 544 self. _ConvertListValueMessage(value, message.list_value)
369 raise ParseError('repeated field {0} must be in [] which is ' 545 elif value is None:
370 '{1}.'.format(name, value)) 546 message.null_value = 0
371 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 547 elif isinstance(value, bool):
372 # Repeated message field. 548 message.bool_value = value
373 for item in value: 549 elif isinstance(value, six.string_types):
374 sub_message = getattr(message, field.name).add() 550 message.string_value = value
375 # None is a null_value in Value. 551 elif isinstance(value, _INT_OR_FLOAT):
376 if (item is None and 552 message.number_value = value
377 sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): 553 else:
378 raise ParseError('null is not allowed to be used as an element' 554 raise ParseError('Unexpected type for Value message.')
379 ' in a repeated field.') 555
380 _ConvertMessage(item, sub_message) 556 def _ConvertListValueMessage(self, value, message):
381 else: 557 """Convert a JSON representation into ListValue message."""
382 # Repeated scalar field. 558 if not isinstance(value, list):
383 for item in value: 559 raise ParseError(
384 if item is None: 560 'ListValue must be in [] which is {0}.'.format(value))
385 raise ParseError('null is not allowed to be used as an element' 561 message.ClearField('values')
386 ' in a repeated field.') 562 for item in value:
387 getattr(message, field.name).append( 563 self._ConvertValueMessage(item, message.values.add())
388 _ConvertScalarFieldValue(item, field)) 564
389 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 565 def _ConvertStructMessage(self, value, message):
390 sub_message = getattr(message, field.name) 566 """Convert a JSON representation into Struct message."""
391 _ConvertMessage(value, sub_message) 567 if not isinstance(value, dict):
568 raise ParseError(
569 'Struct must be in a dict which is {0}.'.format(value))
570 for key in value:
571 self._ConvertValueMessage(value[key], message.fields[key])
572 return
573
574 def _ConvertWrapperMessage(self, value, message):
575 """Convert a JSON representation into Wrapper message."""
576 field = message.DESCRIPTOR.fields_by_name['value']
577 setattr(message, 'value', _ConvertScalarFieldValue(value, field))
578
579 def _ConvertMapFieldValue(self, value, message, field):
580 """Convert map field value for a message map field.
581
582 Args:
583 value: A JSON object to convert the map field value.
584 message: A protocol message to record the converted data.
585 field: The descriptor of the map field to be converted.
586
587 Raises:
588 ParseError: In case of convert problems.
589 """
590 if not isinstance(value, dict):
591 raise ParseError(
592 'Map field {0} must be in a dict which is {1}.'.format(
593 field.name, value))
594 key_field = field.message_type.fields_by_name['key']
595 value_field = field.message_type.fields_by_name['value']
596 for key in value:
597 key_value = _ConvertScalarFieldValue(key, key_field, True)
598 if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
599 self.ConvertMessage(value[key], getattr(
600 message, field.name)[key_value])
392 else: 601 else:
393 setattr(message, field.name, _ConvertScalarFieldValue(value, field)) 602 getattr(message, field.name)[key_value] = _ConvertScalarFieldValue(
394 except ParseError as e: 603 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 604
528 605
529 def _ConvertScalarFieldValue(value, field, require_str=False): 606 def _ConvertScalarFieldValue(value, field, require_str=False):
530 """Convert a single scalar field value. 607 """Convert a single scalar field value.
531 608
532 Args: 609 Args:
533 value: A scalar value to convert the scalar field value. 610 value: A scalar value to convert the scalar field value.
534 field: The descriptor of the field to convert. 611 field: The descriptor of the field to convert.
535 require_str: If True, the field value must be a str. 612 require_str: If True, the field value must be a str.
536 613
537 Returns: 614 Returns:
538 The converted scalar field value 615 The converted scalar field value
539 616
540 Raises: 617 Raises:
541 ParseError: In case of convert problems. 618 ParseError: In case of convert problems.
542 """ 619 """
543 if field.cpp_type in _INT_TYPES: 620 if field.cpp_type in _INT_TYPES:
544 return _ConvertInteger(value) 621 return _ConvertInteger(value)
545 elif field.cpp_type in _FLOAT_TYPES: 622 elif field.cpp_type in _FLOAT_TYPES:
546 return _ConvertFloat(value) 623 return _ConvertFloat(value)
547 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 624 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
548 return _ConvertBool(value, require_str) 625 return _ConvertBool(value, require_str)
549 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 626 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
550 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: 627 if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
551 return base64.b64decode(value) 628 return base64.b64decode(value)
552 else: 629 else:
630 # Checking for unpaired surrogates appears to be unreliable,
631 # depending on the specific Python version, so we check manually.
632 if _UNPAIRED_SURROGATE_PATTERN.search(value):
633 raise ParseError('Unpaired surrogate')
553 return value 634 return value
554 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 635 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
555 # Convert an enum value. 636 # Convert an enum value.
556 enum_value = field.enum_type.values_by_name.get(value, None) 637 enum_value = field.enum_type.values_by_name.get(value, None)
557 if enum_value is None: 638 if enum_value is None:
558 raise ParseError( 639 try:
559 'Enum value must be a string literal with double quotes. ' 640 number = int(value)
560 'Type "{0}" has no value named {1}.'.format( 641 enum_value = field.enum_type.values_by_number.get(number, None)
561 field.enum_type.full_name, value)) 642 except ValueError:
643 raise ParseError('Invalid enum value {0} for enum type {1}.'.format(
644 value, field.enum_type.full_name))
645 if enum_value is None:
646 raise ParseError('Invalid enum value {0} for enum type {1}.'.format(
647 value, field.enum_type.full_name))
562 return enum_value.number 648 return enum_value.number
563 649
564 650
565 def _ConvertInteger(value): 651 def _ConvertInteger(value):
566 """Convert an integer. 652 """Convert an integer.
567 653
568 Args: 654 Args:
569 value: A scalar value to convert. 655 value: A scalar value to convert.
570 656
571 Returns: 657 Returns:
572 The integer value. 658 The integer value.
573 659
574 Raises: 660 Raises:
575 ParseError: If an integer couldn't be consumed. 661 ParseError: If an integer couldn't be consumed.
576 """ 662 """
577 if isinstance(value, float): 663 if isinstance(value, float) and not value.is_integer():
578 raise ParseError('Couldn\'t parse integer: {0}.'.format(value)) 664 raise ParseError('Couldn\'t parse integer: {0}.'.format(value))
579 665
580 if isinstance(value, six.text_type) and value.find(' ') != -1: 666 if isinstance(value, six.text_type) and value.find(' ') != -1:
581 raise ParseError('Couldn\'t parse integer: "{0}".'.format(value)) 667 raise ParseError('Couldn\'t parse integer: "{0}".'.format(value))
582 668
583 return int(value) 669 return int(value)
584 670
585 671
586 def _ConvertFloat(value): 672 def _ConvertFloat(value):
587 """Convert an floating point number.""" 673 """Convert an floating point number."""
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
621 elif value == 'false': 707 elif value == 'false':
622 return False 708 return False
623 else: 709 else:
624 raise ParseError('Expected "true" or "false", not {0}.'.format(value)) 710 raise ParseError('Expected "true" or "false", not {0}.'.format(value))
625 711
626 if not isinstance(value, bool): 712 if not isinstance(value, bool):
627 raise ParseError('Expected true or false without quotes.') 713 raise ParseError('Expected true or false without quotes.')
628 return value 714 return value
629 715
630 _WKTJSONMETHODS = { 716 _WKTJSONMETHODS = {
631 'google.protobuf.Any': [_AnyMessageToJsonObject, 717 'google.protobuf.Any': ['_AnyMessageToJsonObject',
632 _ConvertAnyMessage], 718 '_ConvertAnyMessage'],
633 'google.protobuf.Duration': [_GenericMessageToJsonObject, 719 'google.protobuf.Duration': ['_GenericMessageToJsonObject',
634 _ConvertGenericMessage], 720 '_ConvertGenericMessage'],
635 'google.protobuf.FieldMask': [_GenericMessageToJsonObject, 721 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject',
636 _ConvertGenericMessage], 722 '_ConvertGenericMessage'],
637 'google.protobuf.ListValue': [_ListValueMessageToJsonObject, 723 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject',
638 _ConvertListValueMessage], 724 '_ConvertListValueMessage'],
639 'google.protobuf.Struct': [_StructMessageToJsonObject, 725 'google.protobuf.Struct': ['_StructMessageToJsonObject',
640 _ConvertStructMessage], 726 '_ConvertStructMessage'],
641 'google.protobuf.Timestamp': [_GenericMessageToJsonObject, 727 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject',
642 _ConvertGenericMessage], 728 '_ConvertGenericMessage'],
643 'google.protobuf.Value': [_ValueMessageToJsonObject, 729 'google.protobuf.Value': ['_ValueMessageToJsonObject',
644 _ConvertValueMessage] 730 '_ConvertValueMessage']
645 } 731 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698