diff --git a/proto/message.py b/proto/message.py index 253e6240..7232d42f 100644 --- a/proto/message.py +++ b/proto/message.py @@ -376,7 +376,9 @@ def to_json( use_integers_for_enums=True, including_default_value_fields=True, preserving_proto_field_name=False, + sort_keys=False, indent=2, + float_precision=None, ) -> str: """Given a message instance, serialize it to json @@ -389,10 +391,13 @@ def to_json( preserving_proto_field_name (Optional(bool)): An option that determines whether field name representations preserve proto case (snake_case) or use lowerCamelCase. Default is False. - indent: The JSON object will be pretty-printed with this indent level. + sort_keys (Optional(bool)): If True, then the output will be sorted by field names. + Default is False. + indent (Optional(int)): The JSON object will be pretty-printed with this indent level. An indent level of 0 or negative will only insert newlines. Pass None for the most compact representation without newlines. - + float_precision (Optional(int)): If set, use this to specify float field valid digits. + Default is None. Returns: str: The json string representation of the protocol buffer. """ @@ -401,7 +406,9 @@ def to_json( use_integers_for_enums=use_integers_for_enums, including_default_value_fields=including_default_value_fields, preserving_proto_field_name=preserving_proto_field_name, + sort_keys=sort_keys, indent=indent, + float_precision=float_precision, ) def from_json(cls, payload, *, ignore_unknown_fields=False) -> "Message": @@ -428,6 +435,7 @@ def to_dict( use_integers_for_enums=True, preserving_proto_field_name=True, including_default_value_fields=True, + float_precision=None, ) -> "Message": """Given a message instance, return its representation as a python dict. @@ -443,6 +451,8 @@ def to_dict( including_default_value_fields (Optional(bool)): An option that determines whether the default field values should be included in the results. Default is True. + float_precision (Optional(int)): If set, use this to specify float field valid digits. + Default is None. Returns: dict: A representation of the protocol buffer using pythonic data structures. @@ -454,6 +464,7 @@ def to_dict( including_default_value_fields=including_default_value_fields, preserving_proto_field_name=preserving_proto_field_name, use_integers_for_enums=use_integers_for_enums, + float_precision=float_precision, ) def copy_from(cls, instance, other): diff --git a/tests/test_json.py b/tests/test_json.py index 93ca936c..e94e935a 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -13,6 +13,7 @@ # limitations under the License. import pytest +import re import proto from google.protobuf.json_format import MessageToJson, Parse, ParseError @@ -172,3 +173,26 @@ class Squid(proto.Message): s_two = Squid.from_json(j) assert s == s_two + + +def test_json_sort_keys(): + class Squid(proto.Message): + name = proto.Field(proto.STRING, number=1) + mass_kg = proto.Field(proto.INT32, number=2) + + s = Squid(name="Steve", mass_kg=20) + j = Squid.to_json(s, sort_keys=True, indent=None) + + assert re.search(r"massKg.*name", j) + + +# TODO: https://github.com/googleapis/proto-plus-python/issues/390 +def test_json_float_precision(): + class Squid(proto.Message): + name = proto.Field(proto.STRING, number=1) + mass_kg = proto.Field(proto.FLOAT, number=2) + + s = Squid(name="Steve", mass_kg=3.14159265) + j = Squid.to_json(s, float_precision=3, indent=None) + + assert j == '{"name": "Steve", "massKg": 3.14}' diff --git a/tests/test_message.py b/tests/test_message.py index 3146f0bb..983cde82 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -271,6 +271,17 @@ class Color(proto.Enum): assert new_s == s +# TODO: https://github.com/googleapis/proto-plus-python/issues/390 +def test_serialize_to_dict_float_precision(): + class Squid(proto.Message): + mass_kg = proto.Field(proto.FLOAT, number=1) + + s = Squid(mass_kg=3.14159265) + + s_dict = Squid.to_dict(s, float_precision=3) + assert s_dict["mass_kg"] == 3.14 + + def test_unknown_field_deserialize(): # This is a somewhat common setup: a client uses an older proto definition, # while the server sends the newer definition. The client still needs to be