diff --git a/python/build_targets.bzl b/python/build_targets.bzl index 1014514a4b27a..0cc2911ac09ea 100644 --- a/python/build_targets.bzl +++ b/python/build_targets.bzl @@ -421,6 +421,11 @@ def build_targets(name): srcs = ["google/protobuf/internal/proto_test.py"], ) + internal_py_test( + name = "proto_json_test", + srcs = ["google/protobuf/internal/proto_json_test.py"], + ) + native.cc_library( name = "proto_api", hdrs = ["google/protobuf/proto_api.h"], diff --git a/python/google/protobuf/internal/proto_json_test.py b/python/google/protobuf/internal/proto_json_test.py new file mode 100644 index 0000000000000..b8e59c9e92bde --- /dev/null +++ b/python/google/protobuf/internal/proto_json_test.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Tests Nextgen Pythonic protobuf APIs.""" + +import unittest + +from google.protobuf import proto_json +from google.protobuf.util import json_format_proto3_pb2 + + +class ProtoJsonTest(unittest.TestCase): + + def testSimpleSerialize(self): + message = json_format_proto3_pb2.TestMessage() + message.int32_value = 12345 + expected = {'int32Value': 12345} + self.assertEqual(expected, proto_json.serialize(message)) + + def testSimpleParse(self): + expected = 12345 + js_dict = {'int32Value': expected} + message = proto_json.parse(json_format_proto3_pb2.TestMessage, + js_dict) + self.assertEqual(expected, message.int32_value) + +if __name__ == "__main__": + unittest.main() diff --git a/python/google/protobuf/proto_json.py b/python/google/protobuf/proto_json.py new file mode 100644 index 0000000000000..ea670ab0810bf --- /dev/null +++ b/python/google/protobuf/proto_json.py @@ -0,0 +1,83 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Contains the Nextgen Pythonic Protobuf JSON APIs.""" + +from typing import Optional, Type + +from google.protobuf.message import Message +from google.protobuf.descriptor_pool import DescriptorPool +from google.protobuf import json_format + +def serialize( + message: Message, + always_print_fields_with_no_presence: bool=False, + preserving_proto_field_name: bool=False, + use_integers_for_enums: bool=False, + descriptor_pool: Optional[DescriptorPool]=None, + float_precision: int=None, +) -> dict: + """Converts protobuf message to a dictionary. + + When the dictionary is encoded to JSON, it conforms to proto3 JSON spec. + + Args: + message: The protocol buffers message instance to serialize. + always_print_fields_with_no_presence: If True, fields without + presence (implicit presence scalars, repeated fields, and map fields) will + always be serialized. Any field that supports presence is not affected by + this option (including singular message fields and oneof fields). + preserving_proto_field_name: If True, use the original proto field names as + defined in the .proto file. If False, convert the field names to + lowerCamelCase. + use_integers_for_enums: If true, print integers instead of enum names. + descriptor_pool: A Descriptor Pool for resolving types. If None use the + default. + float_precision: If set, use this to specify float field valid digits. + + Returns: + A dict representation of the protocol buffer message. + """ + return json_format.MessageToDict( + message, + always_print_fields_with_no_presence=always_print_fields_with_no_presence, + preserving_proto_field_name=preserving_proto_field_name, + use_integers_for_enums=use_integers_for_enums, + float_precision=float_precision, + ) + +def parse( + message_class: Type[Message], + js_dict: dict, + ignore_unknown_fields: bool=False, + descriptor_pool: Optional[DescriptorPool]=None, + max_recursion_depth: int=100 +) -> Message: + """Parses a JSON dictionary representation into a message. + + Args: + message_class: The message meta class. + js_dict: Dict representation of a JSON message. + ignore_unknown_fields: If True, do not raise errors for unknown fields. + descriptor_pool: A Descriptor Pool for resolving types. If None use the + default. + max_recursion_depth: max recursion depth of JSON message to be deserialized. + JSON messages over this depth will fail to be deserialized. Default value + is 100. + + Returns: + A new message passed from json_dict. + """ + new_message = message_class() + json_format.ParseDict( + js_dict=js_dict, + message=new_message, + ignore_unknown_fields=ignore_unknown_fields, + descriptor_pool=descriptor_pool, + max_recursion_depth=max_recursion_depth, + ) + return new_message