• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7
8"""Contains routines for printing protocol messages in JSON format.
9
10Simple usage example:
11
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)
15
16  # Parse a json format string to proto object.
17  message = json_format.Parse(json_string, my_proto_pb2.MyMessage())
18"""
19
20__author__ = 'jieluo@google.com (Jie Luo)'
21
22
23import base64
24from collections import OrderedDict
25import json
26import math
27from operator import methodcaller
28import re
29
30from google.protobuf import descriptor
31from google.protobuf import message_factory
32from google.protobuf import symbol_database
33from google.protobuf.internal import type_checkers
34
35
36_INT_TYPES = frozenset([
37    descriptor.FieldDescriptor.CPPTYPE_INT32,
38    descriptor.FieldDescriptor.CPPTYPE_UINT32,
39    descriptor.FieldDescriptor.CPPTYPE_INT64,
40    descriptor.FieldDescriptor.CPPTYPE_UINT64,
41])
42_INT64_TYPES = frozenset([
43    descriptor.FieldDescriptor.CPPTYPE_INT64,
44    descriptor.FieldDescriptor.CPPTYPE_UINT64,
45])
46_FLOAT_TYPES = frozenset([
47    descriptor.FieldDescriptor.CPPTYPE_FLOAT,
48    descriptor.FieldDescriptor.CPPTYPE_DOUBLE,
49])
50_INFINITY = 'Infinity'
51_NEG_INFINITY = '-Infinity'
52_NAN = 'NaN'
53
54_UNPAIRED_SURROGATE_PATTERN = re.compile(
55    '[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]'
56)
57
58_VALID_EXTENSION_NAME = re.compile(r'\[[a-zA-Z0-9\._]*\]$')
59
60
61class Error(Exception):
62  """Top-level module error for json_format."""
63
64
65class SerializeToJsonError(Error):
66  """Thrown if serialization to JSON fails."""
67
68
69class ParseError(Error):
70  """Thrown in case of parsing error."""
71
72
73class EnumStringValueParseError(ParseError):
74  """Thrown if unknown string enum value is encountered.
75  This exception is suppressed if ignore_unknown_fields is set.
76  """
77
78
79def MessageToJson(
80    message,
81    preserving_proto_field_name=False,
82    indent=2,
83    sort_keys=False,
84    use_integers_for_enums=False,
85    descriptor_pool=None,
86    float_precision=None,
87    ensure_ascii=True,
88    always_print_fields_with_no_presence=False,
89):
90  """Converts protobuf message to JSON format.
91
92  Args:
93    message: The protocol buffers message instance to serialize.
94    always_print_fields_with_no_presence: If True, fields without
95      presence (implicit presence scalars, repeated fields, and map fields) will
96      always be serialized. Any field that supports presence is not affected by
97      this option (including singular message fields and oneof fields).
98    preserving_proto_field_name: If True, use the original proto field names as
99      defined in the .proto file. If False, convert the field names to
100      lowerCamelCase.
101    indent: The JSON object will be pretty-printed with this indent level. An
102      indent level of 0 or negative will only insert newlines. If the indent
103      level is None, no newlines will be inserted.
104    sort_keys: If True, then the output will be sorted by field names.
105    use_integers_for_enums: If true, print integers instead of enum names.
106    descriptor_pool: A Descriptor Pool for resolving types. If None use the
107      default.
108    float_precision: If set, use this to specify float field valid digits.
109    ensure_ascii: If True, strings with non-ASCII characters are escaped. If
110      False, Unicode strings are returned unchanged.
111
112  Returns:
113    A string containing the JSON formatted protocol buffer message.
114  """
115  printer = _Printer(
116      preserving_proto_field_name,
117      use_integers_for_enums,
118      descriptor_pool,
119      float_precision,
120      always_print_fields_with_no_presence
121  )
122  return printer.ToJsonString(message, indent, sort_keys, ensure_ascii)
123
124
125def MessageToDict(
126    message,
127    always_print_fields_with_no_presence=False,
128    preserving_proto_field_name=False,
129    use_integers_for_enums=False,
130    descriptor_pool=None,
131    float_precision=None,
132):
133  """Converts protobuf message to a dictionary.
134
135  When the dictionary is encoded to JSON, it conforms to proto3 JSON spec.
136
137  Args:
138    message: The protocol buffers message instance to serialize.
139    always_print_fields_with_no_presence: If True, fields without
140      presence (implicit presence scalars, repeated fields, and map fields) will
141      always be serialized. Any field that supports presence is not affected by
142      this option (including singular message fields and oneof fields).
143    preserving_proto_field_name: If True, use the original proto field names as
144      defined in the .proto file. If False, convert the field names to
145      lowerCamelCase.
146    use_integers_for_enums: If true, print integers instead of enum names.
147    descriptor_pool: A Descriptor Pool for resolving types. If None use the
148      default.
149    float_precision: If set, use this to specify float field valid digits.
150
151  Returns:
152    A dict representation of the protocol buffer message.
153  """
154  printer = _Printer(
155      preserving_proto_field_name,
156      use_integers_for_enums,
157      descriptor_pool,
158      float_precision,
159      always_print_fields_with_no_presence,
160  )
161  # pylint: disable=protected-access
162  return printer._MessageToJsonObject(message)
163
164
165def _IsMapEntry(field):
166  return (
167      field.type == descriptor.FieldDescriptor.TYPE_MESSAGE
168      and field.message_type.has_options
169      and field.message_type.GetOptions().map_entry
170  )
171
172
173class _Printer(object):
174  """JSON format printer for protocol message."""
175
176  def __init__(
177      self,
178      preserving_proto_field_name=False,
179      use_integers_for_enums=False,
180      descriptor_pool=None,
181      float_precision=None,
182      always_print_fields_with_no_presence=False,
183  ):
184    self.always_print_fields_with_no_presence = (
185        always_print_fields_with_no_presence
186    )
187    self.preserving_proto_field_name = preserving_proto_field_name
188    self.use_integers_for_enums = use_integers_for_enums
189    self.descriptor_pool = descriptor_pool
190    if float_precision:
191      self.float_format = '.{}g'.format(float_precision)
192    else:
193      self.float_format = None
194
195  def ToJsonString(self, message, indent, sort_keys, ensure_ascii):
196    js = self._MessageToJsonObject(message)
197    return json.dumps(
198        js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii
199    )
200
201  def _MessageToJsonObject(self, message):
202    """Converts message to an object according to Proto3 JSON Specification."""
203    message_descriptor = message.DESCRIPTOR
204    full_name = message_descriptor.full_name
205    if _IsWrapperMessage(message_descriptor):
206      return self._WrapperMessageToJsonObject(message)
207    if full_name in _WKTJSONMETHODS:
208      return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self)
209    js = {}
210    return self._RegularMessageToJsonObject(message, js)
211
212  def _RegularMessageToJsonObject(self, message, js):
213    """Converts normal message according to Proto3 JSON Specification."""
214    fields = message.ListFields()
215
216    try:
217      for field, value in fields:
218        if self.preserving_proto_field_name:
219          name = field.name
220        else:
221          name = field.json_name
222        if _IsMapEntry(field):
223          # Convert a map field.
224          v_field = field.message_type.fields_by_name['value']
225          js_map = {}
226          for key in value:
227            if isinstance(key, bool):
228              if key:
229                recorded_key = 'true'
230              else:
231                recorded_key = 'false'
232            else:
233              recorded_key = str(key)
234            js_map[recorded_key] = self._FieldToJsonObject(v_field, value[key])
235          js[name] = js_map
236        elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
237          # Convert a repeated field.
238          js[name] = [self._FieldToJsonObject(field, k) for k in value]
239        elif field.is_extension:
240          name = '[%s]' % field.full_name
241          js[name] = self._FieldToJsonObject(field, value)
242        else:
243          js[name] = self._FieldToJsonObject(field, value)
244
245      # Serialize default value if including_default_value_fields is True.
246      if (
247          self.always_print_fields_with_no_presence
248      ):
249        message_descriptor = message.DESCRIPTOR
250        for field in message_descriptor.fields:
251
252          # always_print_fields_with_no_presence doesn't apply to
253          # any field which supports presence.
254          if (
255              self.always_print_fields_with_no_presence
256              and field.has_presence
257          ):
258            continue
259
260          if self.preserving_proto_field_name:
261            name = field.name
262          else:
263            name = field.json_name
264          if name in js:
265            # Skip the field which has been serialized already.
266            continue
267          if _IsMapEntry(field):
268            js[name] = {}
269          elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
270            js[name] = []
271          else:
272            js[name] = self._FieldToJsonObject(field, field.default_value)
273
274    except ValueError as e:
275      raise SerializeToJsonError(
276          'Failed to serialize {0} field: {1}.'.format(field.name, e)
277      ) from e
278
279    return js
280
281  def _FieldToJsonObject(self, field, value):
282    """Converts field value according to Proto3 JSON Specification."""
283    if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
284      return self._MessageToJsonObject(value)
285    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
286      if self.use_integers_for_enums:
287        return value
288      if field.enum_type.full_name == 'google.protobuf.NullValue':
289        return None
290      enum_value = field.enum_type.values_by_number.get(value, None)
291      if enum_value is not None:
292        return enum_value.name
293      else:
294        if field.enum_type.is_closed:
295          raise SerializeToJsonError(
296              'Enum field contains an integer value '
297              'which can not mapped to an enum value.'
298          )
299        else:
300          return value
301    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
302      if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
303        # Use base64 Data encoding for bytes
304        return base64.b64encode(value).decode('utf-8')
305      else:
306        return str(value)
307    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
308      return bool(value)
309    elif field.cpp_type in _INT64_TYPES:
310      return str(value)
311    elif field.cpp_type in _FLOAT_TYPES:
312      if math.isinf(value):
313        if value < 0.0:
314          return _NEG_INFINITY
315        else:
316          return _INFINITY
317      if math.isnan(value):
318        return _NAN
319      if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:
320        if self.float_format:
321          return float(format(value, self.float_format))
322        else:
323          return type_checkers.ToShortestFloat(value)
324
325    return value
326
327  def _AnyMessageToJsonObject(self, message):
328    """Converts Any message according to Proto3 JSON Specification."""
329    if not message.ListFields():
330      return {}
331    # Must print @type first, use OrderedDict instead of {}
332    js = OrderedDict()
333    type_url = message.type_url
334    js['@type'] = type_url
335    sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool)
336    sub_message.ParseFromString(message.value)
337    message_descriptor = sub_message.DESCRIPTOR
338    full_name = message_descriptor.full_name
339    if _IsWrapperMessage(message_descriptor):
340      js['value'] = self._WrapperMessageToJsonObject(sub_message)
341      return js
342    if full_name in _WKTJSONMETHODS:
343      js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0], sub_message)(
344          self
345      )
346      return js
347    return self._RegularMessageToJsonObject(sub_message, js)
348
349  def _GenericMessageToJsonObject(self, message):
350    """Converts message according to Proto3 JSON Specification."""
351    # Duration, Timestamp and FieldMask have ToJsonString method to do the
352    # convert. Users can also call the method directly.
353    return message.ToJsonString()
354
355  def _ValueMessageToJsonObject(self, message):
356    """Converts Value message according to Proto3 JSON Specification."""
357    which = message.WhichOneof('kind')
358    # If the Value message is not set treat as null_value when serialize
359    # to JSON. The parse back result will be different from original message.
360    if which is None or which == 'null_value':
361      return None
362    if which == 'list_value':
363      return self._ListValueMessageToJsonObject(message.list_value)
364    if which == 'number_value':
365      value = message.number_value
366      if math.isinf(value):
367        raise ValueError(
368            'Fail to serialize Infinity for Value.number_value, '
369            'which would parse as string_value'
370        )
371      if math.isnan(value):
372        raise ValueError(
373            'Fail to serialize NaN for Value.number_value, '
374            'which would parse as string_value'
375        )
376    else:
377      value = getattr(message, which)
378    oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]
379    return self._FieldToJsonObject(oneof_descriptor, value)
380
381  def _ListValueMessageToJsonObject(self, message):
382    """Converts ListValue message according to Proto3 JSON Specification."""
383    return [self._ValueMessageToJsonObject(value) for value in message.values]
384
385  def _StructMessageToJsonObject(self, message):
386    """Converts Struct message according to Proto3 JSON Specification."""
387    fields = message.fields
388    ret = {}
389    for key in fields:
390      ret[key] = self._ValueMessageToJsonObject(fields[key])
391    return ret
392
393  def _WrapperMessageToJsonObject(self, message):
394    return self._FieldToJsonObject(
395        message.DESCRIPTOR.fields_by_name['value'], message.value
396    )
397
398
399def _IsWrapperMessage(message_descriptor):
400  return message_descriptor.file.name == 'google/protobuf/wrappers.proto'
401
402
403def _DuplicateChecker(js):
404  result = {}
405  for name, value in js:
406    if name in result:
407      raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name))
408    result[name] = value
409  return result
410
411
412def _CreateMessageFromTypeUrl(type_url, descriptor_pool):
413  """Creates a message from a type URL."""
414  db = symbol_database.Default()
415  pool = db.pool if descriptor_pool is None else descriptor_pool
416  type_name = type_url.split('/')[-1]
417  try:
418    message_descriptor = pool.FindMessageTypeByName(type_name)
419  except KeyError as e:
420    raise TypeError(
421        'Can not find message descriptor by type_url: {0}'.format(type_url)
422    ) from e
423  message_class = message_factory.GetMessageClass(message_descriptor)
424  return message_class()
425
426
427def Parse(
428    text,
429    message,
430    ignore_unknown_fields=False,
431    descriptor_pool=None,
432    max_recursion_depth=100,
433):
434  """Parses a JSON representation of a protocol message into a message.
435
436  Args:
437    text: Message JSON representation.
438    message: A protocol buffer message to merge into.
439    ignore_unknown_fields: If True, do not raise errors for unknown fields.
440    descriptor_pool: A Descriptor Pool for resolving types. If None use the
441      default.
442    max_recursion_depth: max recursion depth of JSON message to be deserialized.
443      JSON messages over this depth will fail to be deserialized. Default value
444      is 100.
445
446  Returns:
447    The same message passed as argument.
448
449  Raises::
450    ParseError: On JSON parsing problems.
451  """
452  if not isinstance(text, str):
453    text = text.decode('utf-8')
454
455  try:
456    js = json.loads(text, object_pairs_hook=_DuplicateChecker)
457  except Exception as e:
458    raise ParseError('Failed to load JSON: {0}.'.format(str(e))) from e
459
460  try:
461    return ParseDict(
462        js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth
463    )
464  except ParseError as e:
465    raise e
466  except Exception as e:
467    raise ParseError(
468        'Failed to parse JSON: {0}: {1}.'.format(type(e).__name__, str(e))
469    ) from e
470
471
472def ParseDict(
473    js_dict,
474    message,
475    ignore_unknown_fields=False,
476    descriptor_pool=None,
477    max_recursion_depth=100,
478):
479  """Parses a JSON dictionary representation into a message.
480
481  Args:
482    js_dict: Dict representation of a JSON message.
483    message: A protocol buffer message to merge into.
484    ignore_unknown_fields: If True, do not raise errors for unknown fields.
485    descriptor_pool: A Descriptor Pool for resolving types. If None use the
486      default.
487    max_recursion_depth: max recursion depth of JSON message to be deserialized.
488      JSON messages over this depth will fail to be deserialized. Default value
489      is 100.
490
491  Returns:
492    The same message passed as argument.
493  """
494  parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth)
495  parser.ConvertMessage(js_dict, message, '')
496  return message
497
498
499_INT_OR_FLOAT = (int, float)
500
501
502class _Parser(object):
503  """JSON format parser for protocol message."""
504
505  def __init__(
506      self, ignore_unknown_fields, descriptor_pool, max_recursion_depth
507  ):
508    self.ignore_unknown_fields = ignore_unknown_fields
509    self.descriptor_pool = descriptor_pool
510    self.max_recursion_depth = max_recursion_depth
511    self.recursion_depth = 0
512
513  def ConvertMessage(self, value, message, path):
514    """Convert a JSON object into a message.
515
516    Args:
517      value: A JSON object.
518      message: A WKT or regular protocol message to record the data.
519      path: parent path to log parse error info.
520
521    Raises:
522      ParseError: In case of convert problems.
523    """
524    self.recursion_depth += 1
525    if self.recursion_depth > self.max_recursion_depth:
526      raise ParseError(
527          'Message too deep. Max recursion depth is {0}'.format(
528              self.max_recursion_depth
529          )
530      )
531    message_descriptor = message.DESCRIPTOR
532    full_name = message_descriptor.full_name
533    if not path:
534      path = message_descriptor.name
535    if _IsWrapperMessage(message_descriptor):
536      self._ConvertWrapperMessage(value, message, path)
537    elif full_name in _WKTJSONMETHODS:
538      methodcaller(_WKTJSONMETHODS[full_name][1], value, message, path)(self)
539    else:
540      self._ConvertFieldValuePair(value, message, path)
541    self.recursion_depth -= 1
542
543  def _ConvertFieldValuePair(self, js, message, path):
544    """Convert field value pairs into regular message.
545
546    Args:
547      js: A JSON object to convert the field value pairs.
548      message: A regular protocol message to record the data.
549      path: parent path to log parse error info.
550
551    Raises:
552      ParseError: In case of problems converting.
553    """
554    names = []
555    message_descriptor = message.DESCRIPTOR
556    fields_by_json_name = dict(
557        (f.json_name, f) for f in message_descriptor.fields
558    )
559    for name in js:
560      try:
561        field = fields_by_json_name.get(name, None)
562        if not field:
563          field = message_descriptor.fields_by_name.get(name, None)
564        if not field and _VALID_EXTENSION_NAME.match(name):
565          if not message_descriptor.is_extendable:
566            raise ParseError(
567                'Message type {0} does not have extensions at {1}'.format(
568                    message_descriptor.full_name, path
569                )
570            )
571          identifier = name[1:-1]  # strip [] brackets
572          # pylint: disable=protected-access
573          field = message.Extensions._FindExtensionByName(identifier)
574          # pylint: enable=protected-access
575          if not field:
576            # Try looking for extension by the message type name, dropping the
577            # field name following the final . separator in full_name.
578            identifier = '.'.join(identifier.split('.')[:-1])
579            # pylint: disable=protected-access
580            field = message.Extensions._FindExtensionByName(identifier)
581            # pylint: enable=protected-access
582        if not field:
583          if self.ignore_unknown_fields:
584            continue
585          raise ParseError(
586              (
587                  'Message type "{0}" has no field named "{1}" at "{2}".\n'
588                  ' Available Fields(except extensions): "{3}"'
589              ).format(
590                  message_descriptor.full_name,
591                  name,
592                  path,
593                  [f.json_name for f in message_descriptor.fields],
594              )
595          )
596        if name in names:
597          raise ParseError(
598              'Message type "{0}" should not have multiple '
599              '"{1}" fields at "{2}".'.format(
600                  message.DESCRIPTOR.full_name, name, path
601              )
602          )
603        names.append(name)
604        value = js[name]
605        # Check no other oneof field is parsed.
606        if field.containing_oneof is not None and value is not None:
607          oneof_name = field.containing_oneof.name
608          if oneof_name in names:
609            raise ParseError(
610                'Message type "{0}" should not have multiple '
611                '"{1}" oneof fields at "{2}".'.format(
612                    message.DESCRIPTOR.full_name, oneof_name, path
613                )
614            )
615          names.append(oneof_name)
616
617        if value is None:
618          if (
619              field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE
620              and field.message_type.full_name == 'google.protobuf.Value'
621          ):
622            sub_message = getattr(message, field.name)
623            sub_message.null_value = 0
624          elif (
625              field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM
626              and field.enum_type.full_name == 'google.protobuf.NullValue'
627          ):
628            setattr(message, field.name, 0)
629          else:
630            message.ClearField(field.name)
631          continue
632
633        # Parse field value.
634        if _IsMapEntry(field):
635          message.ClearField(field.name)
636          self._ConvertMapFieldValue(
637              value, message, field, '{0}.{1}'.format(path, name)
638          )
639        elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
640          message.ClearField(field.name)
641          if not isinstance(value, list):
642            raise ParseError(
643                'repeated field {0} must be in [] which is {1} at {2}'.format(
644                    name, value, path
645                )
646            )
647          if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
648            # Repeated message field.
649            for index, item in enumerate(value):
650              sub_message = getattr(message, field.name).add()
651              # None is a null_value in Value.
652              if (
653                  item is None
654                  and sub_message.DESCRIPTOR.full_name
655                  != 'google.protobuf.Value'
656              ):
657                raise ParseError(
658                    'null is not allowed to be used as an element'
659                    ' in a repeated field at {0}.{1}[{2}]'.format(
660                        path, name, index
661                    )
662                )
663              self.ConvertMessage(
664                  item, sub_message, '{0}.{1}[{2}]'.format(path, name, index)
665              )
666          else:
667            # Repeated scalar field.
668            for index, item in enumerate(value):
669              if item is None:
670                raise ParseError(
671                    'null is not allowed to be used as an element'
672                    ' in a repeated field at {0}.{1}[{2}]'.format(
673                        path, name, index
674                    )
675                )
676              self._ConvertAndAppendScalar(
677                message, field, item, '{0}.{1}[{2}]'.format(path, name, index))
678        elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
679          if field.is_extension:
680            sub_message = message.Extensions[field]
681          else:
682            sub_message = getattr(message, field.name)
683          sub_message.SetInParent()
684          self.ConvertMessage(value, sub_message, '{0}.{1}'.format(path, name))
685        else:
686          if field.is_extension:
687            self._ConvertAndSetScalarExtension(message, field, value, '{0}.{1}'.format(path, name))
688          else:
689            self._ConvertAndSetScalar(message, field, value, '{0}.{1}'.format(path, name))
690      except ParseError as e:
691        if field and field.containing_oneof is None:
692          raise ParseError(
693              'Failed to parse {0} field: {1}.'.format(name, e)
694          ) from e
695        else:
696          raise ParseError(str(e)) from e
697      except ValueError as e:
698        raise ParseError(
699            'Failed to parse {0} field: {1}.'.format(name, e)
700        ) from e
701      except TypeError as e:
702        raise ParseError(
703            'Failed to parse {0} field: {1}.'.format(name, e)
704        ) from e
705
706  def _ConvertAnyMessage(self, value, message, path):
707    """Convert a JSON representation into Any message."""
708    if isinstance(value, dict) and not value:
709      return
710    try:
711      type_url = value['@type']
712    except KeyError as e:
713      raise ParseError(
714          '@type is missing when parsing any message at {0}'.format(path)
715      ) from e
716
717    try:
718      sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool)
719    except TypeError as e:
720      raise ParseError('{0} at {1}'.format(e, path)) from e
721    message_descriptor = sub_message.DESCRIPTOR
722    full_name = message_descriptor.full_name
723    if _IsWrapperMessage(message_descriptor):
724      self._ConvertWrapperMessage(
725          value['value'], sub_message, '{0}.value'.format(path)
726      )
727    elif full_name in _WKTJSONMETHODS:
728      methodcaller(
729          _WKTJSONMETHODS[full_name][1],
730          value['value'],
731          sub_message,
732          '{0}.value'.format(path),
733      )(self)
734    else:
735      del value['@type']
736      self._ConvertFieldValuePair(value, sub_message, path)
737      value['@type'] = type_url
738    # Sets Any message
739    message.value = sub_message.SerializeToString()
740    message.type_url = type_url
741
742  def _ConvertGenericMessage(self, value, message, path):
743    """Convert a JSON representation into message with FromJsonString."""
744    # Duration, Timestamp, FieldMask have a FromJsonString method to do the
745    # conversion. Users can also call the method directly.
746    try:
747      message.FromJsonString(value)
748    except ValueError as e:
749      raise ParseError('{0} at {1}'.format(e, path)) from e
750
751  def _ConvertValueMessage(self, value, message, path):
752    """Convert a JSON representation into Value message."""
753    if isinstance(value, dict):
754      self._ConvertStructMessage(value, message.struct_value, path)
755    elif isinstance(value, list):
756      self._ConvertListValueMessage(value, message.list_value, path)
757    elif value is None:
758      message.null_value = 0
759    elif isinstance(value, bool):
760      message.bool_value = value
761    elif isinstance(value, str):
762      message.string_value = value
763    elif isinstance(value, _INT_OR_FLOAT):
764      message.number_value = value
765    else:
766      raise ParseError(
767          'Value {0} has unexpected type {1} at {2}'.format(
768              value, type(value), path
769          )
770      )
771
772  def _ConvertListValueMessage(self, value, message, path):
773    """Convert a JSON representation into ListValue message."""
774    if not isinstance(value, list):
775      raise ParseError(
776          'ListValue must be in [] which is {0} at {1}'.format(value, path)
777      )
778    message.ClearField('values')
779    for index, item in enumerate(value):
780      self._ConvertValueMessage(
781          item, message.values.add(), '{0}[{1}]'.format(path, index)
782      )
783
784  def _ConvertStructMessage(self, value, message, path):
785    """Convert a JSON representation into Struct message."""
786    if not isinstance(value, dict):
787      raise ParseError(
788          'Struct must be in a dict which is {0} at {1}'.format(value, path)
789      )
790    # Clear will mark the struct as modified so it will be created even if
791    # there are no values.
792    message.Clear()
793    for key in value:
794      self._ConvertValueMessage(
795          value[key], message.fields[key], '{0}.{1}'.format(path, key)
796      )
797    return
798
799  def _ConvertWrapperMessage(self, value, message, path):
800    """Convert a JSON representation into Wrapper message."""
801    field = message.DESCRIPTOR.fields_by_name['value']
802    self._ConvertAndSetScalar(message, field, value, path='{0}.value'.format(path))
803
804  def _ConvertMapFieldValue(self, value, message, field, path):
805    """Convert map field value for a message map field.
806
807    Args:
808      value: A JSON object to convert the map field value.
809      message: A protocol message to record the converted data.
810      field: The descriptor of the map field to be converted.
811      path: parent path to log parse error info.
812
813    Raises:
814      ParseError: In case of convert problems.
815    """
816    if not isinstance(value, dict):
817      raise ParseError(
818          'Map field {0} must be in a dict which is {1} at {2}'.format(
819              field.name, value, path
820          )
821      )
822    key_field = field.message_type.fields_by_name['key']
823    value_field = field.message_type.fields_by_name['value']
824    for key in value:
825      key_value = _ConvertScalarFieldValue(
826          key, key_field, '{0}.key'.format(path), True
827      )
828      if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
829        self.ConvertMessage(
830            value[key],
831            getattr(message, field.name)[key_value],
832            '{0}[{1}]'.format(path, key_value),
833        )
834      else:
835        self._ConvertAndSetScalarToMapKey(
836            message,
837            field,
838            key_value,
839            value[key],
840            path='{0}[{1}]'.format(path, key_value))
841
842  def _ConvertAndSetScalarExtension(self, message, extension_field, js_value, path):
843    """Convert scalar from js_value and assign it to message.Extensions[extension_field]."""
844    try:
845      message.Extensions[extension_field] = _ConvertScalarFieldValue(
846          js_value, extension_field, path)
847    except EnumStringValueParseError:
848      if not self.ignore_unknown_fields:
849        raise
850
851  def _ConvertAndSetScalar(self, message, field, js_value, path):
852    """Convert scalar from js_value and assign it to message.field."""
853    try:
854      setattr(
855          message,
856          field.name,
857          _ConvertScalarFieldValue(js_value, field, path))
858    except EnumStringValueParseError:
859      if not self.ignore_unknown_fields:
860        raise
861
862  def _ConvertAndAppendScalar(self, message, repeated_field, js_value, path):
863    """Convert scalar from js_value and append it to message.repeated_field."""
864    try:
865      getattr(message, repeated_field.name).append(
866          _ConvertScalarFieldValue(js_value, repeated_field, path))
867    except EnumStringValueParseError:
868      if not self.ignore_unknown_fields:
869        raise
870
871  def _ConvertAndSetScalarToMapKey(self, message, map_field, converted_key, js_value, path):
872    """Convert scalar from 'js_value' and add it to message.map_field[converted_key]."""
873    try:
874      getattr(message, map_field.name)[converted_key] = _ConvertScalarFieldValue(
875          js_value, map_field.message_type.fields_by_name['value'], path,
876      )
877    except EnumStringValueParseError:
878      if not self.ignore_unknown_fields:
879        raise
880
881
882def _ConvertScalarFieldValue(value, field, path, require_str=False):
883  """Convert a single scalar field value.
884
885  Args:
886    value: A scalar value to convert the scalar field value.
887    field: The descriptor of the field to convert.
888    path: parent path to log parse error info.
889    require_str: If True, the field value must be a str.
890
891  Returns:
892    The converted scalar field value
893
894  Raises:
895    ParseError: In case of convert problems.
896    EnumStringValueParseError: In case of unknown enum string value.
897  """
898  try:
899    if field.cpp_type in _INT_TYPES:
900      return _ConvertInteger(value)
901    elif field.cpp_type in _FLOAT_TYPES:
902      return _ConvertFloat(value, field)
903    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
904      return _ConvertBool(value, require_str)
905    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
906      if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
907        if isinstance(value, str):
908          encoded = value.encode('utf-8')
909        else:
910          encoded = value
911        # Add extra padding '='
912        padded_value = encoded + b'=' * (4 - len(encoded) % 4)
913        return base64.urlsafe_b64decode(padded_value)
914      else:
915        # Checking for unpaired surrogates appears to be unreliable,
916        # depending on the specific Python version, so we check manually.
917        if _UNPAIRED_SURROGATE_PATTERN.search(value):
918          raise ParseError('Unpaired surrogate')
919        return value
920    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
921      # Convert an enum value.
922      enum_value = field.enum_type.values_by_name.get(value, None)
923      if enum_value is None:
924        try:
925          number = int(value)
926          enum_value = field.enum_type.values_by_number.get(number, None)
927        except ValueError as e:
928          # Since parsing to integer failed and lookup in values_by_name didn't
929          # find this name, we have an enum string value which is unknown.
930          raise EnumStringValueParseError(
931              'Invalid enum value {0} for enum type {1}'.format(
932                  value, field.enum_type.full_name
933              )
934          ) from e
935        if enum_value is None:
936          if field.enum_type.is_closed:
937            raise ParseError(
938                'Invalid enum value {0} for enum type {1}'.format(
939                    value, field.enum_type.full_name
940                )
941            )
942          else:
943            return number
944      return enum_value.number
945  except EnumStringValueParseError as e:
946    raise EnumStringValueParseError('{0} at {1}'.format(e, path)) from e
947  except ParseError as e:
948    raise ParseError('{0} at {1}'.format(e, path)) from e
949
950
951def _ConvertInteger(value):
952  """Convert an integer.
953
954  Args:
955    value: A scalar value to convert.
956
957  Returns:
958    The integer value.
959
960  Raises:
961    ParseError: If an integer couldn't be consumed.
962  """
963  if isinstance(value, float) and not value.is_integer():
964    raise ParseError("Couldn't parse integer: {0}".format(value))
965
966  if isinstance(value, str) and value.find(' ') != -1:
967    raise ParseError('Couldn\'t parse integer: "{0}"'.format(value))
968
969  if isinstance(value, bool):
970    raise ParseError(
971        'Bool value {0} is not acceptable for integer field'.format(value)
972    )
973
974  return int(value)
975
976
977def _ConvertFloat(value, field):
978  """Convert an floating point number."""
979  if isinstance(value, float):
980    if math.isnan(value):
981      raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead')
982    if math.isinf(value):
983      if value > 0:
984        raise ParseError(
985            "Couldn't parse Infinity or value too large, "
986            'use quoted "Infinity" instead'
987        )
988      else:
989        raise ParseError(
990            "Couldn't parse -Infinity or value too small, "
991            'use quoted "-Infinity" instead'
992        )
993    if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:
994      # pylint: disable=protected-access
995      if value > type_checkers._FLOAT_MAX:
996        raise ParseError('Float value too large')
997      # pylint: disable=protected-access
998      if value < type_checkers._FLOAT_MIN:
999        raise ParseError('Float value too small')
1000  if value == 'nan':
1001    raise ParseError('Couldn\'t parse float "nan", use "NaN" instead')
1002  try:
1003    # Assume Python compatible syntax.
1004    return float(value)
1005  except ValueError as e:
1006    # Check alternative spellings.
1007    if value == _NEG_INFINITY:
1008      return float('-inf')
1009    elif value == _INFINITY:
1010      return float('inf')
1011    elif value == _NAN:
1012      return float('nan')
1013    else:
1014      raise ParseError("Couldn't parse float: {0}".format(value)) from e
1015
1016
1017def _ConvertBool(value, require_str):
1018  """Convert a boolean value.
1019
1020  Args:
1021    value: A scalar value to convert.
1022    require_str: If True, value must be a str.
1023
1024  Returns:
1025    The bool parsed.
1026
1027  Raises:
1028    ParseError: If a boolean value couldn't be consumed.
1029  """
1030  if require_str:
1031    if value == 'true':
1032      return True
1033    elif value == 'false':
1034      return False
1035    else:
1036      raise ParseError('Expected "true" or "false", not {0}'.format(value))
1037
1038  if not isinstance(value, bool):
1039    raise ParseError('Expected true or false without quotes')
1040  return value
1041
1042
1043_WKTJSONMETHODS = {
1044    'google.protobuf.Any': ['_AnyMessageToJsonObject', '_ConvertAnyMessage'],
1045    'google.protobuf.Duration': [
1046        '_GenericMessageToJsonObject',
1047        '_ConvertGenericMessage',
1048    ],
1049    'google.protobuf.FieldMask': [
1050        '_GenericMessageToJsonObject',
1051        '_ConvertGenericMessage',
1052    ],
1053    'google.protobuf.ListValue': [
1054        '_ListValueMessageToJsonObject',
1055        '_ConvertListValueMessage',
1056    ],
1057    'google.protobuf.Struct': [
1058        '_StructMessageToJsonObject',
1059        '_ConvertStructMessage',
1060    ],
1061    'google.protobuf.Timestamp': [
1062        '_GenericMessageToJsonObject',
1063        '_ConvertGenericMessage',
1064    ],
1065    'google.protobuf.Value': [
1066        '_ValueMessageToJsonObject',
1067        '_ConvertValueMessage',
1068    ],
1069}
1070