31 """Contains routines for printing protocol messages in JSON format.
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)
39 # Parse a json format string to proto object.
40 message = json_format.Parse(json_string, my_proto_pb2.MyMessage())
43 __author__ =
'jieluo@google.com (Jie Luo)'
47 from collections
import OrderedDict
50 from operator
import methodcaller
59 _TIMESTAMPFOMAT =
'%Y-%m-%dT%H:%M:%S'
60 _INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32,
61 descriptor.FieldDescriptor.CPPTYPE_UINT32,
62 descriptor.FieldDescriptor.CPPTYPE_INT64,
63 descriptor.FieldDescriptor.CPPTYPE_UINT64])
64 _INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64,
65 descriptor.FieldDescriptor.CPPTYPE_UINT64])
66 _FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT,
67 descriptor.FieldDescriptor.CPPTYPE_DOUBLE])
68 _INFINITY =
'Infinity'
69 _NEG_INFINITY =
'-Infinity'
72 _UNPAIRED_SURROGATE_PATTERN = re.compile(
73 u'[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]')
75 _VALID_EXTENSION_NAME = re.compile(
r'\[[a-zA-Z0-9\._]*\]$')
78 class Error(Exception):
79 """Top-level module error for json_format."""
82 class SerializeToJsonError(Error):
83 """Thrown if serialization to JSON fails."""
86 class ParseError(Error):
87 """Thrown in case of parsing error."""
92 including_default_value_fields=False,
93 preserving_proto_field_name=False,
96 use_integers_for_enums=False,
98 float_precision=None):
99 """Converts protobuf message to JSON format.
102 message: The protocol buffers message instance to serialize.
103 including_default_value_fields: If True, singular primitive fields,
104 repeated fields, and map fields will always be serialized. If
105 False, only serialize non-empty fields. Singular message fields
106 and oneof fields are not affected by this option.
107 preserving_proto_field_name: If True, use the original proto field
108 names as defined in the .proto file. If False, convert the field
109 names to lowerCamelCase.
110 indent: The JSON object will be pretty-printed with this indent level.
111 An indent level of 0 or negative will only insert newlines.
112 sort_keys: If True, then the output will be sorted by field names.
113 use_integers_for_enums: If true, print integers instead of enum names.
114 descriptor_pool: A Descriptor Pool for resolving types. If None use the
116 float_precision: If set, use this to specify float field valid digits.
119 A string containing the JSON formatted protocol buffer message.
122 including_default_value_fields,
123 preserving_proto_field_name,
124 use_integers_for_enums,
126 float_precision=float_precision)
127 return printer.ToJsonString(message, indent, sort_keys)
132 including_default_value_fields=False,
133 preserving_proto_field_name=False,
134 use_integers_for_enums=False,
135 descriptor_pool=None,
136 float_precision=None):
137 """Converts protobuf message to a dictionary.
139 When the dictionary is encoded to JSON, it conforms to proto3 JSON spec.
142 message: The protocol buffers message instance to serialize.
143 including_default_value_fields: If True, singular primitive fields,
144 repeated fields, and map fields will always be serialized. If
145 False, only serialize non-empty fields. Singular message fields
146 and oneof fields are not affected by this option.
147 preserving_proto_field_name: If True, use the original proto field
148 names as defined in the .proto file. If False, convert the field
149 names to lowerCamelCase.
150 use_integers_for_enums: If true, print integers instead of enum names.
151 descriptor_pool: A Descriptor Pool for resolving types. If None use the
153 float_precision: If set, use this to specify float field valid digits.
156 A dict representation of the protocol buffer message.
159 including_default_value_fields,
160 preserving_proto_field_name,
161 use_integers_for_enums,
163 float_precision=float_precision)
165 return printer._MessageToJsonObject(message)
169 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE
and
170 field.message_type.has_options
and
171 field.message_type.GetOptions().map_entry)
175 """JSON format printer for protocol message."""
179 including_default_value_fields=False,
180 preserving_proto_field_name=False,
181 use_integers_for_enums=False,
182 descriptor_pool=None,
183 float_precision=None):
195 return json.dumps(js, indent=indent, sort_keys=sort_keys)
198 """Converts message to an object according to Proto3 JSON Specification."""
199 message_descriptor = message.DESCRIPTOR
200 full_name = message_descriptor.full_name
203 if full_name
in _WKTJSONMETHODS:
204 return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self)
209 """Converts normal message according to Proto3 JSON Specification."""
210 fields = message.ListFields()
213 for field, value
in fields:
217 name = field.json_name
220 v_field = field.message_type.fields_by_name[
'value']
223 if isinstance(key, bool):
225 recorded_key =
'true'
227 recorded_key =
'false'
229 recorded_key =
str(key)
233 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
237 elif field.is_extension:
238 name =
'[%s]' % field.full_name
245 message_descriptor = message.DESCRIPTOR
246 for field
in message_descriptor.fields:
248 if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED
and
249 field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE)
or
250 field.containing_oneof):
255 name = field.json_name
261 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
266 except ValueError
as e:
268 'Failed to serialize {0} field: {1}.'.
format(field.name, e))
273 """Converts field value according to Proto3 JSON Specification."""
274 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
276 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
279 if field.enum_type.full_name ==
'google.protobuf.NullValue':
281 enum_value = field.enum_type.values_by_number.get(value,
None)
282 if enum_value
is not None:
283 return enum_value.name
285 if field.file.syntax ==
'proto3':
288 'which can not mapped to an enum value.')
289 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
290 if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
292 return base64.b64encode(value).
decode(
'utf-8')
295 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
297 elif field.cpp_type
in _INT64_TYPES:
299 elif field.cpp_type
in _FLOAT_TYPES:
300 if math.isinf(value):
305 if math.isnan(value):
307 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:
311 return type_checkers.ToShortestFloat(value)
316 """Converts Any message according to Proto3 JSON Specification."""
317 if not message.ListFields():
321 type_url = message.type_url
322 js[
'@type'] = type_url
324 sub_message.ParseFromString(message.value)
325 message_descriptor = sub_message.DESCRIPTOR
326 full_name = message_descriptor.full_name
330 if full_name
in _WKTJSONMETHODS:
331 js[
'value'] = methodcaller(_WKTJSONMETHODS[full_name][0],
337 """Converts message according to Proto3 JSON Specification."""
340 return message.ToJsonString()
343 """Converts Value message according to Proto3 JSON Specification."""
344 which = message.WhichOneof(
'kind')
347 if which
is None or which ==
'null_value':
349 if which ==
'list_value':
351 if which ==
'struct_value':
352 value = message.struct_value
354 value = getattr(message, which)
355 oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]
359 """Converts ListValue message according to Proto3 JSON Specification."""
361 for value
in message.values]
364 """Converts Struct message according to Proto3 JSON Specification."""
365 fields = message.fields
373 message.DESCRIPTOR.fields_by_name[
'value'], message.value)
377 return message_descriptor.file.name ==
'google/protobuf/wrappers.proto'
382 for name, value
in js:
390 """Creates a message from a type URL."""
391 db = symbol_database.Default()
392 pool = db.pool
if descriptor_pool
is None else descriptor_pool
393 type_name = type_url.split(
'/')[-1]
395 message_descriptor = pool.FindMessageTypeByName(type_name)
398 'Can not find message descriptor by type_url: {0}.'.
format(type_url))
399 message_class = db.GetPrototype(message_descriptor)
400 return message_class()
403 def Parse(text, message, ignore_unknown_fields=False, descriptor_pool=None):
404 """Parses a JSON representation of a protocol message into a message.
407 text: Message JSON representation.
408 message: A protocol buffer message to merge into.
409 ignore_unknown_fields: If True, do not raise errors for unknown fields.
410 descriptor_pool: A Descriptor Pool for resolving types. If None use the
414 The same message passed as argument.
417 ParseError: On JSON parsing problems.
419 if not isinstance(text, str):
420 text = text.decode(
'utf-8')
422 js = json.loads(text, object_pairs_hook=_DuplicateChecker)
423 except ValueError
as e:
425 return ParseDict(js, message, ignore_unknown_fields, descriptor_pool)
430 ignore_unknown_fields=False,
431 descriptor_pool=None):
432 """Parses a JSON dictionary representation into a message.
435 js_dict: Dict representation of a JSON message.
436 message: A protocol buffer message to merge into.
437 ignore_unknown_fields: If True, do not raise errors for unknown fields.
438 descriptor_pool: A Descriptor Pool for resolving types. If None use the
442 The same message passed as argument.
444 parser =
_Parser(ignore_unknown_fields, descriptor_pool)
445 parser.ConvertMessage(js_dict, message)
449 _INT_OR_FLOAT = (int, float)
453 """JSON format parser for protocol message."""
455 def __init__(self, ignore_unknown_fields, descriptor_pool):
460 """Convert a JSON object into a message.
463 value: A JSON object.
464 message: A WKT or regular protocol message to record the data.
467 ParseError: In case of convert problems.
469 message_descriptor = message.DESCRIPTOR
470 full_name = message_descriptor.full_name
473 elif full_name
in _WKTJSONMETHODS:
474 methodcaller(_WKTJSONMETHODS[full_name][1], value, message)(self)
479 """Convert field value pairs into regular message.
482 js: A JSON object to convert the field value pairs.
483 message: A regular protocol message to record the data.
486 ParseError: In case of problems converting.
489 message_descriptor = message.DESCRIPTOR
490 fields_by_json_name = dict((f.json_name, f)
491 for f
in message_descriptor.fields)
494 field = fields_by_json_name.get(name,
None)
496 field = message_descriptor.fields_by_name.get(name,
None)
497 if not field
and _VALID_EXTENSION_NAME.match(name):
498 if not message_descriptor.is_extendable:
500 message_descriptor.full_name))
501 identifier = name[1:-1]
503 field = message.Extensions._FindExtensionByName(identifier)
508 identifier =
'.'.join(identifier.split(
'.')[:-1])
510 field = message.Extensions._FindExtensionByName(identifier)
516 (
'Message type "{0}" has no field named "{1}".\n'
517 ' Available Fields(except extensions): {2}').
format(
518 message_descriptor.full_name, name,
519 [f.json_name
for f
in message_descriptor.fields]))
521 raise ParseError(
'Message type "{0}" should not have multiple '
523 message.DESCRIPTOR.full_name, name))
527 if field.containing_oneof
is not None and value
is not None:
528 oneof_name = field.containing_oneof.name
529 if oneof_name
in names:
530 raise ParseError(
'Message type "{0}" should not have multiple '
531 '"{1}" oneof fields.'.
format(
532 message.DESCRIPTOR.full_name, oneof_name))
533 names.append(oneof_name)
536 if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE
537 and field.message_type.full_name ==
'google.protobuf.Value'):
538 sub_message = getattr(message, field.name)
539 sub_message.null_value = 0
540 elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM
541 and field.enum_type.full_name ==
'google.protobuf.NullValue'):
542 setattr(message, field.name, 0)
544 message.ClearField(field.name)
549 message.ClearField(field.name)
551 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
552 message.ClearField(field.name)
553 if not isinstance(value, list):
554 raise ParseError(
'repeated field {0} must be in [] which is '
555 '{1}.'.
format(name, value))
556 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
559 sub_message = getattr(message, field.name).
add()
562 sub_message.DESCRIPTOR.full_name !=
'google.protobuf.Value'):
563 raise ParseError(
'null is not allowed to be used as an element'
564 ' in a repeated field.')
570 raise ParseError(
'null is not allowed to be used as an element'
571 ' in a repeated field.')
572 getattr(message, field.name).append(
574 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
575 if field.is_extension:
576 sub_message = message.Extensions[field]
578 sub_message = getattr(message, field.name)
579 sub_message.SetInParent()
582 if field.is_extension:
586 except ParseError
as e:
587 if field
and field.containing_oneof
is None:
591 except ValueError
as e:
593 except TypeError
as e:
597 """Convert a JSON representation into Any message."""
598 if isinstance(value, dict)
and not value:
601 type_url = value[
'@type']
603 raise ParseError(
'@type is missing when parsing any message.')
606 message_descriptor = sub_message.DESCRIPTOR
607 full_name = message_descriptor.full_name
610 elif full_name
in _WKTJSONMETHODS:
612 _WKTJSONMETHODS[full_name][1], value[
'value'], sub_message)(self)
616 value[
'@type'] = type_url
618 message.value = sub_message.SerializeToString()
619 message.type_url = type_url
622 """Convert a JSON representation into message with FromJsonString."""
626 message.FromJsonString(value)
627 except ValueError
as e:
631 """Convert a JSON representation into Value message."""
632 if isinstance(value, dict):
634 elif isinstance(value, list):
637 message.null_value = 0
638 elif isinstance(value, bool):
639 message.bool_value = value
640 elif isinstance(value, str):
641 message.string_value = value
642 elif isinstance(value, _INT_OR_FLOAT):
643 message.number_value = value
649 """Convert a JSON representation into ListValue message."""
650 if not isinstance(value, list):
652 'ListValue must be in [] which is {0}.'.
format(value))
653 message.ClearField(
'values')
658 """Convert a JSON representation into Struct message."""
659 if not isinstance(value, dict):
661 'Struct must be in a dict which is {0}.'.
format(value))
670 """Convert a JSON representation into Wrapper message."""
671 field = message.DESCRIPTOR.fields_by_name[
'value']
675 """Convert map field value for a message map field.
678 value: A JSON object to convert the map field value.
679 message: A protocol message to record the converted data.
680 field: The descriptor of the map field to be converted.
683 ParseError: In case of convert problems.
685 if not isinstance(value, dict):
687 'Map field {0} must be in a dict which is {1}.'.
format(
689 key_field = field.message_type.fields_by_name[
'key']
690 value_field = field.message_type.fields_by_name[
'value']
693 if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
695 message, field.name)[key_value])
698 value[key], value_field)
702 """Convert a single scalar field value.
705 value: A scalar value to convert the scalar field value.
706 field: The descriptor of the field to convert.
707 require_str: If True, the field value must be a str.
710 The converted scalar field value
713 ParseError: In case of convert problems.
715 if field.cpp_type
in _INT_TYPES:
717 elif field.cpp_type
in _FLOAT_TYPES:
719 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
721 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
722 if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
723 if isinstance(value, str):
724 encoded = value.encode(
'utf-8')
728 padded_value = encoded + b
'=' * (4 -
len(encoded) % 4)
729 return base64.urlsafe_b64decode(padded_value)
733 if _UNPAIRED_SURROGATE_PATTERN.search(value):
736 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
738 enum_value = field.enum_type.values_by_name.get(value,
None)
739 if enum_value
is None:
742 enum_value = field.enum_type.values_by_number.get(number,
None)
745 value, field.enum_type.full_name))
746 if enum_value
is None:
747 if field.file.syntax ==
'proto3':
751 value, field.enum_type.full_name))
752 return enum_value.number
756 """Convert an integer.
759 value: A scalar value to convert.
765 ParseError: If an integer couldn't be consumed.
767 if isinstance(value, float)
and not value.is_integer():
770 if isinstance(value, str)
and value.find(
' ') != -1:
773 if isinstance(value, bool):
774 raise ParseError(
'Bool value {0} is not acceptable for '
775 'integer field.'.
format(value))
781 """Convert an floating point number."""
782 if isinstance(value, float):
783 if math.isnan(value):
784 raise ParseError(
'Couldn\'t parse NaN, use quoted "NaN" instead.')
785 if math.isinf(value):
787 raise ParseError(
'Couldn\'t parse Infinity or value too large, '
788 'use quoted "Infinity" instead.')
790 raise ParseError(
'Couldn\'t parse -Infinity or value too small, '
791 'use quoted "-Infinity" instead.')
792 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:
794 if value > type_checkers._FLOAT_MAX:
797 if value < type_checkers._FLOAT_MIN:
800 raise ParseError(
'Couldn\'t parse float "nan", use "NaN" instead.')
806 if value == _NEG_INFINITY:
808 elif value == _INFINITY:
817 """Convert a boolean value.
820 value: A scalar value to convert.
821 require_str: If True, value must be a str.
827 ParseError: If a boolean value couldn't be consumed.
832 elif value ==
'false':
837 if not isinstance(value, bool):
838 raise ParseError(
'Expected true or false without quotes.')
842 'google.protobuf.Any': [
'_AnyMessageToJsonObject',
843 '_ConvertAnyMessage'],
844 'google.protobuf.Duration': [
'_GenericMessageToJsonObject',
845 '_ConvertGenericMessage'],
846 'google.protobuf.FieldMask': [
'_GenericMessageToJsonObject',
847 '_ConvertGenericMessage'],
848 'google.protobuf.ListValue': [
'_ListValueMessageToJsonObject',
849 '_ConvertListValueMessage'],
850 'google.protobuf.Struct': [
'_StructMessageToJsonObject',
851 '_ConvertStructMessage'],
852 'google.protobuf.Timestamp': [
'_GenericMessageToJsonObject',
853 '_ConvertGenericMessage'],
854 'google.protobuf.Value': [
'_ValueMessageToJsonObject',
855 '_ConvertValueMessage']