Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/protobuf/json_format.py: 14%
440 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:45 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:45 +0000
1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc. All rights reserved.
3#
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file or at
6# https://developers.google.com/open-source/licenses/bsd
8"""Contains routines for printing protocol messages in JSON format.
10Simple usage example:
12 # Create a proto object and serialize it to a json format string.
13 message = my_proto_pb2.MyMessage(foo='bar')
14 json_string = json_format.MessageToJson(message)
16 # Parse a json format string to proto object.
17 message = json_format.Parse(json_string, my_proto_pb2.MyMessage())
18"""
20__author__ = 'jieluo@google.com (Jie Luo)'
23import base64
24from collections import OrderedDict
25import json
26import math
27from operator import methodcaller
28import re
30from google.protobuf.internal import type_checkers
31from google.protobuf import descriptor
32from google.protobuf import message_factory
33from google.protobuf import symbol_database
36_INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32,
37 descriptor.FieldDescriptor.CPPTYPE_UINT32,
38 descriptor.FieldDescriptor.CPPTYPE_INT64,
39 descriptor.FieldDescriptor.CPPTYPE_UINT64])
40_INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64,
41 descriptor.FieldDescriptor.CPPTYPE_UINT64])
42_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT,
43 descriptor.FieldDescriptor.CPPTYPE_DOUBLE])
44_INFINITY = 'Infinity'
45_NEG_INFINITY = '-Infinity'
46_NAN = 'NaN'
48_UNPAIRED_SURROGATE_PATTERN = re.compile(
49 u'[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]')
51_VALID_EXTENSION_NAME = re.compile(r'\[[a-zA-Z0-9\._]*\]$')
54class Error(Exception):
55 """Top-level module error for json_format."""
58class SerializeToJsonError(Error):
59 """Thrown if serialization to JSON fails."""
62class ParseError(Error):
63 """Thrown in case of parsing error."""
66def MessageToJson(
67 message,
68 including_default_value_fields=False,
69 preserving_proto_field_name=False,
70 indent=2,
71 sort_keys=False,
72 use_integers_for_enums=False,
73 descriptor_pool=None,
74 float_precision=None,
75 ensure_ascii=True):
76 """Converts protobuf message to JSON format.
78 Args:
79 message: The protocol buffers message instance to serialize.
80 including_default_value_fields: If True, singular primitive fields,
81 repeated fields, and map fields will always be serialized. If
82 False, only serialize non-empty fields. Singular message fields
83 and oneof fields are not affected by this option.
84 preserving_proto_field_name: If True, use the original proto field
85 names as defined in the .proto file. If False, convert the field
86 names to lowerCamelCase.
87 indent: The JSON object will be pretty-printed with this indent level.
88 An indent level of 0 or negative will only insert newlines. If the
89 indent level is None, no newlines will be inserted.
90 sort_keys: If True, then the output will be sorted by field names.
91 use_integers_for_enums: If true, print integers instead of enum names.
92 descriptor_pool: A Descriptor Pool for resolving types. If None use the
93 default.
94 float_precision: If set, use this to specify float field valid digits.
95 ensure_ascii: If True, strings with non-ASCII characters are escaped.
96 If False, Unicode strings are returned unchanged.
98 Returns:
99 A string containing the JSON formatted protocol buffer message.
100 """
101 printer = _Printer(
102 including_default_value_fields,
103 preserving_proto_field_name,
104 use_integers_for_enums,
105 descriptor_pool,
106 float_precision=float_precision)
107 return printer.ToJsonString(message, indent, sort_keys, ensure_ascii)
110def MessageToDict(
111 message,
112 including_default_value_fields=False,
113 preserving_proto_field_name=False,
114 use_integers_for_enums=False,
115 descriptor_pool=None,
116 float_precision=None):
117 """Converts protobuf message to a dictionary.
119 When the dictionary is encoded to JSON, it conforms to proto3 JSON spec.
121 Args:
122 message: The protocol buffers message instance to serialize.
123 including_default_value_fields: If True, singular primitive fields,
124 repeated fields, and map fields will always be serialized. If
125 False, only serialize non-empty fields. Singular message fields
126 and oneof fields are not affected by this option.
127 preserving_proto_field_name: If True, use the original proto field
128 names as defined in the .proto file. If False, convert the field
129 names to lowerCamelCase.
130 use_integers_for_enums: If true, print integers instead of enum names.
131 descriptor_pool: A Descriptor Pool for resolving types. If None use the
132 default.
133 float_precision: If set, use this to specify float field valid digits.
135 Returns:
136 A dict representation of the protocol buffer message.
137 """
138 printer = _Printer(
139 including_default_value_fields,
140 preserving_proto_field_name,
141 use_integers_for_enums,
142 descriptor_pool,
143 float_precision=float_precision)
144 # pylint: disable=protected-access
145 return printer._MessageToJsonObject(message)
148def _IsMapEntry(field):
149 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
150 field.message_type.has_options and
151 field.message_type.GetOptions().map_entry)
154class _Printer(object):
155 """JSON format printer for protocol message."""
157 def __init__(
158 self,
159 including_default_value_fields=False,
160 preserving_proto_field_name=False,
161 use_integers_for_enums=False,
162 descriptor_pool=None,
163 float_precision=None):
164 self.including_default_value_fields = including_default_value_fields
165 self.preserving_proto_field_name = preserving_proto_field_name
166 self.use_integers_for_enums = use_integers_for_enums
167 self.descriptor_pool = descriptor_pool
168 if float_precision:
169 self.float_format = '.{}g'.format(float_precision)
170 else:
171 self.float_format = None
173 def ToJsonString(self, message, indent, sort_keys, ensure_ascii):
174 js = self._MessageToJsonObject(message)
175 return json.dumps(
176 js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii)
178 def _MessageToJsonObject(self, message):
179 """Converts message to an object according to Proto3 JSON Specification."""
180 message_descriptor = message.DESCRIPTOR
181 full_name = message_descriptor.full_name
182 if _IsWrapperMessage(message_descriptor):
183 return self._WrapperMessageToJsonObject(message)
184 if full_name in _WKTJSONMETHODS:
185 return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self)
186 js = {}
187 return self._RegularMessageToJsonObject(message, js)
189 def _RegularMessageToJsonObject(self, message, js):
190 """Converts normal message according to Proto3 JSON Specification."""
191 fields = message.ListFields()
193 try:
194 for field, value in fields:
195 if self.preserving_proto_field_name:
196 name = field.name
197 else:
198 name = field.json_name
199 if _IsMapEntry(field):
200 # Convert a map field.
201 v_field = field.message_type.fields_by_name['value']
202 js_map = {}
203 for key in value:
204 if isinstance(key, bool):
205 if key:
206 recorded_key = 'true'
207 else:
208 recorded_key = 'false'
209 else:
210 recorded_key = str(key)
211 js_map[recorded_key] = self._FieldToJsonObject(
212 v_field, value[key])
213 js[name] = js_map
214 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
215 # Convert a repeated field.
216 js[name] = [self._FieldToJsonObject(field, k)
217 for k in value]
218 elif field.is_extension:
219 name = '[%s]' % field.full_name
220 js[name] = self._FieldToJsonObject(field, value)
221 else:
222 js[name] = self._FieldToJsonObject(field, value)
224 # Serialize default value if including_default_value_fields is True.
225 if self.including_default_value_fields:
226 message_descriptor = message.DESCRIPTOR
227 for field in message_descriptor.fields:
228 # Singular message fields and oneof fields will not be affected.
229 if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and
230 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or
231 field.containing_oneof):
232 continue
233 if self.preserving_proto_field_name:
234 name = field.name
235 else:
236 name = field.json_name
237 if name in js:
238 # Skip the field which has been serialized already.
239 continue
240 if _IsMapEntry(field):
241 js[name] = {}
242 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
243 js[name] = []
244 else:
245 js[name] = self._FieldToJsonObject(field, field.default_value)
247 except ValueError as e:
248 raise SerializeToJsonError(
249 'Failed to serialize {0} field: {1}.'.format(field.name, e)) from e
251 return js
253 def _FieldToJsonObject(self, field, value):
254 """Converts field value according to Proto3 JSON Specification."""
255 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
256 return self._MessageToJsonObject(value)
257 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
258 if self.use_integers_for_enums:
259 return value
260 if field.enum_type.full_name == 'google.protobuf.NullValue':
261 return None
262 enum_value = field.enum_type.values_by_number.get(value, None)
263 if enum_value is not None:
264 return enum_value.name
265 else:
266 if field.enum_type.is_closed:
267 raise SerializeToJsonError('Enum field contains an integer value '
268 'which can not mapped to an enum value.')
269 else:
270 return value
271 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
272 if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
273 # Use base64 Data encoding for bytes
274 return base64.b64encode(value).decode('utf-8')
275 else:
276 return value
277 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
278 return bool(value)
279 elif field.cpp_type in _INT64_TYPES:
280 return str(value)
281 elif field.cpp_type in _FLOAT_TYPES:
282 if math.isinf(value):
283 if value < 0.0:
284 return _NEG_INFINITY
285 else:
286 return _INFINITY
287 if math.isnan(value):
288 return _NAN
289 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:
290 if self.float_format:
291 return float(format(value, self.float_format))
292 else:
293 return type_checkers.ToShortestFloat(value)
295 return value
297 def _AnyMessageToJsonObject(self, message):
298 """Converts Any message according to Proto3 JSON Specification."""
299 if not message.ListFields():
300 return {}
301 # Must print @type first, use OrderedDict instead of {}
302 js = OrderedDict()
303 type_url = message.type_url
304 js['@type'] = type_url
305 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool)
306 sub_message.ParseFromString(message.value)
307 message_descriptor = sub_message.DESCRIPTOR
308 full_name = message_descriptor.full_name
309 if _IsWrapperMessage(message_descriptor):
310 js['value'] = self._WrapperMessageToJsonObject(sub_message)
311 return js
312 if full_name in _WKTJSONMETHODS:
313 js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0],
314 sub_message)(self)
315 return js
316 return self._RegularMessageToJsonObject(sub_message, js)
318 def _GenericMessageToJsonObject(self, message):
319 """Converts message according to Proto3 JSON Specification."""
320 # Duration, Timestamp and FieldMask have ToJsonString method to do the
321 # convert. Users can also call the method directly.
322 return message.ToJsonString()
324 def _ValueMessageToJsonObject(self, message):
325 """Converts Value message according to Proto3 JSON Specification."""
326 which = message.WhichOneof('kind')
327 # If the Value message is not set treat as null_value when serialize
328 # to JSON. The parse back result will be different from original message.
329 if which is None or which == 'null_value':
330 return None
331 if which == 'list_value':
332 return self._ListValueMessageToJsonObject(message.list_value)
333 if which == 'number_value':
334 value = message.number_value
335 if math.isinf(value):
336 raise ValueError('Fail to serialize Infinity for Value.number_value, '
337 'which would parse as string_value')
338 if math.isnan(value):
339 raise ValueError('Fail to serialize NaN for Value.number_value, '
340 'which would parse as string_value')
341 else:
342 value = getattr(message, which)
343 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]
344 return self._FieldToJsonObject(oneof_descriptor, value)
346 def _ListValueMessageToJsonObject(self, message):
347 """Converts ListValue message according to Proto3 JSON Specification."""
348 return [self._ValueMessageToJsonObject(value)
349 for value in message.values]
351 def _StructMessageToJsonObject(self, message):
352 """Converts Struct message according to Proto3 JSON Specification."""
353 fields = message.fields
354 ret = {}
355 for key in fields:
356 ret[key] = self._ValueMessageToJsonObject(fields[key])
357 return ret
359 def _WrapperMessageToJsonObject(self, message):
360 return self._FieldToJsonObject(
361 message.DESCRIPTOR.fields_by_name['value'], message.value)
364def _IsWrapperMessage(message_descriptor):
365 return message_descriptor.file.name == 'google/protobuf/wrappers.proto'
368def _DuplicateChecker(js):
369 result = {}
370 for name, value in js:
371 if name in result:
372 raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name))
373 result[name] = value
374 return result
377def _CreateMessageFromTypeUrl(type_url, descriptor_pool):
378 """Creates a message from a type URL."""
379 db = symbol_database.Default()
380 pool = db.pool if descriptor_pool is None else descriptor_pool
381 type_name = type_url.split('/')[-1]
382 try:
383 message_descriptor = pool.FindMessageTypeByName(type_name)
384 except KeyError as e:
385 raise TypeError(
386 'Can not find message descriptor by type_url: {0}'.format(type_url)
387 ) from e
388 message_class = message_factory.GetMessageClass(message_descriptor)
389 return message_class()
392def Parse(text,
393 message,
394 ignore_unknown_fields=False,
395 descriptor_pool=None,
396 max_recursion_depth=100):
397 """Parses a JSON representation of a protocol message into a message.
399 Args:
400 text: Message JSON representation.
401 message: A protocol buffer message to merge into.
402 ignore_unknown_fields: If True, do not raise errors for unknown fields.
403 descriptor_pool: A Descriptor Pool for resolving types. If None use the
404 default.
405 max_recursion_depth: max recursion depth of JSON message to be
406 deserialized. JSON messages over this depth will fail to be
407 deserialized. Default value is 100.
409 Returns:
410 The same message passed as argument.
412 Raises::
413 ParseError: On JSON parsing problems.
414 """
415 if not isinstance(text, str):
416 text = text.decode('utf-8')
417 try:
418 js = json.loads(text, object_pairs_hook=_DuplicateChecker)
419 except ValueError as e:
420 raise ParseError('Failed to load JSON: {0}.'.format(str(e))) from e
421 return ParseDict(js, message, ignore_unknown_fields, descriptor_pool,
422 max_recursion_depth)
425def ParseDict(js_dict,
426 message,
427 ignore_unknown_fields=False,
428 descriptor_pool=None,
429 max_recursion_depth=100):
430 """Parses a JSON dictionary representation into a message.
432 Args:
433 js_dict: Dict representation of a JSON message.
434 message: A protocol buffer message to merge into.
435 ignore_unknown_fields: If True, do not raise errors for unknown fields.
436 descriptor_pool: A Descriptor Pool for resolving types. If None use the
437 default.
438 max_recursion_depth: max recursion depth of JSON message to be
439 deserialized. JSON messages over this depth will fail to be
440 deserialized. Default value is 100.
442 Returns:
443 The same message passed as argument.
444 """
445 parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth)
446 parser.ConvertMessage(js_dict, message, '')
447 return message
450_INT_OR_FLOAT = (int, float)
453class _Parser(object):
454 """JSON format parser for protocol message."""
456 def __init__(self, ignore_unknown_fields, descriptor_pool,
457 max_recursion_depth):
458 self.ignore_unknown_fields = ignore_unknown_fields
459 self.descriptor_pool = descriptor_pool
460 self.max_recursion_depth = max_recursion_depth
461 self.recursion_depth = 0
463 def ConvertMessage(self, value, message, path):
464 """Convert a JSON object into a message.
466 Args:
467 value: A JSON object.
468 message: A WKT or regular protocol message to record the data.
469 path: parent path to log parse error info.
471 Raises:
472 ParseError: In case of convert problems.
473 """
474 self.recursion_depth += 1
475 if self.recursion_depth > self.max_recursion_depth:
476 raise ParseError('Message too deep. Max recursion depth is {0}'.format(
477 self.max_recursion_depth))
478 message_descriptor = message.DESCRIPTOR
479 full_name = message_descriptor.full_name
480 if not path:
481 path = message_descriptor.name
482 if _IsWrapperMessage(message_descriptor):
483 self._ConvertWrapperMessage(value, message, path)
484 elif full_name in _WKTJSONMETHODS:
485 methodcaller(_WKTJSONMETHODS[full_name][1], value, message, path)(self)
486 else:
487 self._ConvertFieldValuePair(value, message, path)
488 self.recursion_depth -= 1
490 def _ConvertFieldValuePair(self, js, message, path):
491 """Convert field value pairs into regular message.
493 Args:
494 js: A JSON object to convert the field value pairs.
495 message: A regular protocol message to record the data.
496 path: parent path to log parse error info.
498 Raises:
499 ParseError: In case of problems converting.
500 """
501 names = []
502 message_descriptor = message.DESCRIPTOR
503 fields_by_json_name = dict((f.json_name, f)
504 for f in message_descriptor.fields)
505 for name in js:
506 try:
507 field = fields_by_json_name.get(name, None)
508 if not field:
509 field = message_descriptor.fields_by_name.get(name, None)
510 if not field and _VALID_EXTENSION_NAME.match(name):
511 if not message_descriptor.is_extendable:
512 raise ParseError(
513 'Message type {0} does not have extensions at {1}'.format(
514 message_descriptor.full_name, path))
515 identifier = name[1:-1] # strip [] brackets
516 # pylint: disable=protected-access
517 field = message.Extensions._FindExtensionByName(identifier)
518 # pylint: enable=protected-access
519 if not field:
520 # Try looking for extension by the message type name, dropping the
521 # field name following the final . separator in full_name.
522 identifier = '.'.join(identifier.split('.')[:-1])
523 # pylint: disable=protected-access
524 field = message.Extensions._FindExtensionByName(identifier)
525 # pylint: enable=protected-access
526 if not field:
527 if self.ignore_unknown_fields:
528 continue
529 raise ParseError(
530 ('Message type "{0}" has no field named "{1}" at "{2}".\n'
531 ' Available Fields(except extensions): "{3}"').format(
532 message_descriptor.full_name, name, path,
533 [f.json_name for f in message_descriptor.fields]))
534 if name in names:
535 raise ParseError('Message type "{0}" should not have multiple '
536 '"{1}" fields at "{2}".'.format(
537 message.DESCRIPTOR.full_name, name, path))
538 names.append(name)
539 value = js[name]
540 # Check no other oneof field is parsed.
541 if field.containing_oneof is not None and value is not None:
542 oneof_name = field.containing_oneof.name
543 if oneof_name in names:
544 raise ParseError('Message type "{0}" should not have multiple '
545 '"{1}" oneof fields at "{2}".'.format(
546 message.DESCRIPTOR.full_name, oneof_name,
547 path))
548 names.append(oneof_name)
550 if value is None:
551 if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE
552 and field.message_type.full_name == 'google.protobuf.Value'):
553 sub_message = getattr(message, field.name)
554 sub_message.null_value = 0
555 elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM
556 and field.enum_type.full_name == 'google.protobuf.NullValue'):
557 setattr(message, field.name, 0)
558 else:
559 message.ClearField(field.name)
560 continue
562 # Parse field value.
563 if _IsMapEntry(field):
564 message.ClearField(field.name)
565 self._ConvertMapFieldValue(value, message, field,
566 '{0}.{1}'.format(path, name))
567 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
568 message.ClearField(field.name)
569 if not isinstance(value, list):
570 raise ParseError('repeated field {0} must be in [] which is '
571 '{1} at {2}'.format(name, value, path))
572 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
573 # Repeated message field.
574 for index, item in enumerate(value):
575 sub_message = getattr(message, field.name).add()
576 # None is a null_value in Value.
577 if (item is None and
578 sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'):
579 raise ParseError('null is not allowed to be used as an element'
580 ' in a repeated field at {0}.{1}[{2}]'.format(
581 path, name, index))
582 self.ConvertMessage(item, sub_message,
583 '{0}.{1}[{2}]'.format(path, name, index))
584 else:
585 # Repeated scalar field.
586 for index, item in enumerate(value):
587 if item is None:
588 raise ParseError('null is not allowed to be used as an element'
589 ' in a repeated field at {0}.{1}[{2}]'.format(
590 path, name, index))
591 getattr(message, field.name).append(
592 _ConvertScalarFieldValue(
593 item, field, '{0}.{1}[{2}]'.format(path, name, index)))
594 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
595 if field.is_extension:
596 sub_message = message.Extensions[field]
597 else:
598 sub_message = getattr(message, field.name)
599 sub_message.SetInParent()
600 self.ConvertMessage(value, sub_message, '{0}.{1}'.format(path, name))
601 else:
602 if field.is_extension:
603 message.Extensions[field] = _ConvertScalarFieldValue(
604 value, field, '{0}.{1}'.format(path, name))
605 else:
606 setattr(
607 message, field.name,
608 _ConvertScalarFieldValue(value, field,
609 '{0}.{1}'.format(path, name)))
610 except ParseError as e:
611 if field and field.containing_oneof is None:
612 raise ParseError(
613 'Failed to parse {0} field: {1}.'.format(name, e)
614 ) from e
615 else:
616 raise ParseError(str(e)) from e
617 except ValueError as e:
618 raise ParseError(
619 'Failed to parse {0} field: {1}.'.format(name, e)
620 ) from e
621 except TypeError as e:
622 raise ParseError(
623 'Failed to parse {0} field: {1}.'.format(name, e)
624 ) from e
626 def _ConvertAnyMessage(self, value, message, path):
627 """Convert a JSON representation into Any message."""
628 if isinstance(value, dict) and not value:
629 return
630 try:
631 type_url = value['@type']
632 except KeyError as e:
633 raise ParseError(
634 '@type is missing when parsing any message at {0}'.format(path)
635 ) from e
637 try:
638 sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool)
639 except TypeError as e:
640 raise ParseError('{0} at {1}'.format(e, path)) from e
641 message_descriptor = sub_message.DESCRIPTOR
642 full_name = message_descriptor.full_name
643 if _IsWrapperMessage(message_descriptor):
644 self._ConvertWrapperMessage(value['value'], sub_message,
645 '{0}.value'.format(path))
646 elif full_name in _WKTJSONMETHODS:
647 methodcaller(_WKTJSONMETHODS[full_name][1], value['value'], sub_message,
648 '{0}.value'.format(path))(
649 self)
650 else:
651 del value['@type']
652 self._ConvertFieldValuePair(value, sub_message, path)
653 value['@type'] = type_url
654 # Sets Any message
655 message.value = sub_message.SerializeToString()
656 message.type_url = type_url
658 def _ConvertGenericMessage(self, value, message, path):
659 """Convert a JSON representation into message with FromJsonString."""
660 # Duration, Timestamp, FieldMask have a FromJsonString method to do the
661 # conversion. Users can also call the method directly.
662 try:
663 message.FromJsonString(value)
664 except ValueError as e:
665 raise ParseError('{0} at {1}'.format(e, path)) from e
667 def _ConvertValueMessage(self, value, message, path):
668 """Convert a JSON representation into Value message."""
669 if isinstance(value, dict):
670 self._ConvertStructMessage(value, message.struct_value, path)
671 elif isinstance(value, list):
672 self._ConvertListValueMessage(value, message.list_value, path)
673 elif value is None:
674 message.null_value = 0
675 elif isinstance(value, bool):
676 message.bool_value = value
677 elif isinstance(value, str):
678 message.string_value = value
679 elif isinstance(value, _INT_OR_FLOAT):
680 message.number_value = value
681 else:
682 raise ParseError('Value {0} has unexpected type {1} at {2}'.format(
683 value, type(value), path))
685 def _ConvertListValueMessage(self, value, message, path):
686 """Convert a JSON representation into ListValue message."""
687 if not isinstance(value, list):
688 raise ParseError('ListValue must be in [] which is {0} at {1}'.format(
689 value, path))
690 message.ClearField('values')
691 for index, item in enumerate(value):
692 self._ConvertValueMessage(item, message.values.add(),
693 '{0}[{1}]'.format(path, index))
695 def _ConvertStructMessage(self, value, message, path):
696 """Convert a JSON representation into Struct message."""
697 if not isinstance(value, dict):
698 raise ParseError('Struct must be in a dict which is {0} at {1}'.format(
699 value, path))
700 # Clear will mark the struct as modified so it will be created even if
701 # there are no values.
702 message.Clear()
703 for key in value:
704 self._ConvertValueMessage(value[key], message.fields[key],
705 '{0}.{1}'.format(path, key))
706 return
708 def _ConvertWrapperMessage(self, value, message, path):
709 """Convert a JSON representation into Wrapper message."""
710 field = message.DESCRIPTOR.fields_by_name['value']
711 setattr(
712 message, 'value',
713 _ConvertScalarFieldValue(value, field, path='{0}.value'.format(path)))
715 def _ConvertMapFieldValue(self, value, message, field, path):
716 """Convert map field value for a message map field.
718 Args:
719 value: A JSON object to convert the map field value.
720 message: A protocol message to record the converted data.
721 field: The descriptor of the map field to be converted.
722 path: parent path to log parse error info.
724 Raises:
725 ParseError: In case of convert problems.
726 """
727 if not isinstance(value, dict):
728 raise ParseError(
729 'Map field {0} must be in a dict which is {1} at {2}'.format(
730 field.name, value, path))
731 key_field = field.message_type.fields_by_name['key']
732 value_field = field.message_type.fields_by_name['value']
733 for key in value:
734 key_value = _ConvertScalarFieldValue(key, key_field,
735 '{0}.key'.format(path), True)
736 if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
737 self.ConvertMessage(value[key],
738 getattr(message, field.name)[key_value],
739 '{0}[{1}]'.format(path, key_value))
740 else:
741 getattr(message, field.name)[key_value] = _ConvertScalarFieldValue(
742 value[key], value_field, path='{0}[{1}]'.format(path, key_value))
745def _ConvertScalarFieldValue(value, field, path, require_str=False):
746 """Convert a single scalar field value.
748 Args:
749 value: A scalar value to convert the scalar field value.
750 field: The descriptor of the field to convert.
751 path: parent path to log parse error info.
752 require_str: If True, the field value must be a str.
754 Returns:
755 The converted scalar field value
757 Raises:
758 ParseError: In case of convert problems.
759 """
760 try:
761 if field.cpp_type in _INT_TYPES:
762 return _ConvertInteger(value)
763 elif field.cpp_type in _FLOAT_TYPES:
764 return _ConvertFloat(value, field)
765 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
766 return _ConvertBool(value, require_str)
767 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
768 if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
769 if isinstance(value, str):
770 encoded = value.encode('utf-8')
771 else:
772 encoded = value
773 # Add extra padding '='
774 padded_value = encoded + b'=' * (4 - len(encoded) % 4)
775 return base64.urlsafe_b64decode(padded_value)
776 else:
777 # Checking for unpaired surrogates appears to be unreliable,
778 # depending on the specific Python version, so we check manually.
779 if _UNPAIRED_SURROGATE_PATTERN.search(value):
780 raise ParseError('Unpaired surrogate')
781 return value
782 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
783 # Convert an enum value.
784 enum_value = field.enum_type.values_by_name.get(value, None)
785 if enum_value is None:
786 try:
787 number = int(value)
788 enum_value = field.enum_type.values_by_number.get(number, None)
789 except ValueError as e:
790 raise ParseError('Invalid enum value {0} for enum type {1}'.format(
791 value, field.enum_type.full_name)) from e
792 if enum_value is None:
793 if field.enum_type.is_closed:
794 raise ParseError('Invalid enum value {0} for enum type {1}'.format(
795 value, field.enum_type.full_name))
796 else:
797 return number
798 return enum_value.number
799 except ParseError as e:
800 raise ParseError('{0} at {1}'.format(e, path)) from e
803def _ConvertInteger(value):
804 """Convert an integer.
806 Args:
807 value: A scalar value to convert.
809 Returns:
810 The integer value.
812 Raises:
813 ParseError: If an integer couldn't be consumed.
814 """
815 if isinstance(value, float) and not value.is_integer():
816 raise ParseError('Couldn\'t parse integer: {0}'.format(value))
818 if isinstance(value, str) and value.find(' ') != -1:
819 raise ParseError('Couldn\'t parse integer: "{0}"'.format(value))
821 if isinstance(value, bool):
822 raise ParseError('Bool value {0} is not acceptable for '
823 'integer field'.format(value))
825 return int(value)
828def _ConvertFloat(value, field):
829 """Convert an floating point number."""
830 if isinstance(value, float):
831 if math.isnan(value):
832 raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead')
833 if math.isinf(value):
834 if value > 0:
835 raise ParseError('Couldn\'t parse Infinity or value too large, '
836 'use quoted "Infinity" instead')
837 else:
838 raise ParseError('Couldn\'t parse -Infinity or value too small, '
839 'use quoted "-Infinity" instead')
840 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:
841 # pylint: disable=protected-access
842 if value > type_checkers._FLOAT_MAX:
843 raise ParseError('Float value too large')
844 # pylint: disable=protected-access
845 if value < type_checkers._FLOAT_MIN:
846 raise ParseError('Float value too small')
847 if value == 'nan':
848 raise ParseError('Couldn\'t parse float "nan", use "NaN" instead')
849 try:
850 # Assume Python compatible syntax.
851 return float(value)
852 except ValueError as e:
853 # Check alternative spellings.
854 if value == _NEG_INFINITY:
855 return float('-inf')
856 elif value == _INFINITY:
857 return float('inf')
858 elif value == _NAN:
859 return float('nan')
860 else:
861 raise ParseError('Couldn\'t parse float: {0}'.format(value)) from e
864def _ConvertBool(value, require_str):
865 """Convert a boolean value.
867 Args:
868 value: A scalar value to convert.
869 require_str: If True, value must be a str.
871 Returns:
872 The bool parsed.
874 Raises:
875 ParseError: If a boolean value couldn't be consumed.
876 """
877 if require_str:
878 if value == 'true':
879 return True
880 elif value == 'false':
881 return False
882 else:
883 raise ParseError('Expected "true" or "false", not {0}'.format(value))
885 if not isinstance(value, bool):
886 raise ParseError('Expected true or false without quotes')
887 return value
889_WKTJSONMETHODS = {
890 'google.protobuf.Any': ['_AnyMessageToJsonObject',
891 '_ConvertAnyMessage'],
892 'google.protobuf.Duration': ['_GenericMessageToJsonObject',
893 '_ConvertGenericMessage'],
894 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject',
895 '_ConvertGenericMessage'],
896 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject',
897 '_ConvertListValueMessage'],
898 'google.protobuf.Struct': ['_StructMessageToJsonObject',
899 '_ConvertStructMessage'],
900 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject',
901 '_ConvertGenericMessage'],
902 'google.protobuf.Value': ['_ValueMessageToJsonObject',
903 '_ConvertValueMessage']
904}