Skip to content

Commit

Permalink
Pull down the include_default_values argument to to_json (#405)
Browse files Browse the repository at this point in the history
  • Loading branch information
SamuelYvon authored Aug 8, 2022
1 parent f31d51c commit 591ec5e
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
21 changes: 19 additions & 2 deletions src/betterproto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,12 @@ def from_dict(self: T, value: Dict[str, Any]) -> T:
setattr(self, field_name, v)
return self

def to_json(self, indent: Union[None, int, str] = None) -> str:
def to_json(
self,
indent: Union[None, int, str] = None,
include_default_values: bool = False,
casing: Casing = Casing.CAMEL,
) -> str:
"""A helper function to parse the message instance into its JSON
representation.
Expand All @@ -1273,12 +1278,24 @@ def to_json(self, indent: Union[None, int, str] = None) -> str:
indent: Optional[Union[:class:`int`, :class:`str`]]
The indent to pass to :func:`json.dumps`.
include_default_values: :class:`bool`
If ``True`` will include the default values of fields. Default is ``False``.
E.g. an ``int32`` field will be included with a value of ``0`` if this is
set to ``True``, otherwise this would be ignored.
casing: :class:`Casing`
The casing to use for key values. Default is :attr:`Casing.CAMEL` for
compatibility purposes.
Returns
--------
:class:`str`
The JSON representation of the message.
"""
return json.dumps(self.to_dict(), indent=indent)
return json.dumps(
self.to_dict(include_default_values=include_default_values, casing=casing),
indent=indent,
)

def from_json(self: T, value: Union[str, bytes]) -> T:
"""A helper function to return the message instance from its JSON
Expand Down
63 changes: 63 additions & 0 deletions tests/test_features.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from copy import (
copy,
deepcopy,
Expand Down Expand Up @@ -190,6 +191,37 @@ class CasingTest(betterproto.Message):

assert test == CasingTest(1, 2, 3, 4)

# Serializing should be strict.
assert json.loads(test.to_json()) == {
"pascalCase": 1,
"camelCase": 2,
"snakeCase": 3,
"kabobCase": 4,
}

assert json.loads(test.to_json(casing=betterproto.Casing.SNAKE)) == {
"pascal_case": 1,
"camel_case": 2,
"snake_case": 3,
"kabob_case": 4,
}


def test_dict_casing():
@dataclass
class CasingTest(betterproto.Message):
pascal_case: int = betterproto.int32_field(1)
camel_case: int = betterproto.int32_field(2)
snake_case: int = betterproto.int32_field(3)
kabob_case: int = betterproto.int32_field(4)

# Parsing should accept almost any input
test = CasingTest().from_dict(
{"PascalCase": 1, "camelCase": 2, "snake_case": 3, "kabob-case": 4}
)

assert test == CasingTest(1, 2, 3, 4)

# Serializing should be strict.
assert test.to_dict() == {
"pascalCase": 1,
Expand Down Expand Up @@ -233,6 +265,37 @@ class Request(betterproto.Message):
assert Request().parse(b"\n\x00").flag is False


def test_to_json_default_values():
@dataclass
class TestMessage(betterproto.Message):
some_int: int = betterproto.int32_field(1)
some_double: float = betterproto.double_field(2)
some_str: str = betterproto.string_field(3)
some_bool: bool = betterproto.bool_field(4)

# Empty dict
test = TestMessage().from_dict({})

assert json.loads(test.to_json(include_default_values=True)) == {
"someInt": 0,
"someDouble": 0.0,
"someStr": "",
"someBool": False,
}

# All default values
test = TestMessage().from_dict(
{"someInt": 0, "someDouble": 0.0, "someStr": "", "someBool": False}
)

assert json.loads(test.to_json(include_default_values=True)) == {
"someInt": 0,
"someDouble": 0.0,
"someStr": "",
"someBool": False,
}


def test_to_dict_default_values():
@dataclass
class TestMessage(betterproto.Message):
Expand Down

0 comments on commit 591ec5e

Please sign in to comment.