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