Skip to content

Commit 8f46136

Browse files
committed
Implement skip_if_false field attribute
1 parent 6293675 commit 8f46136

File tree

8 files changed

+66
-18
lines changed

8 files changed

+66
-18
lines changed

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ From Json: Hoge(i=10, s='hoge', f=100.0, b=True)
6262
* Field attributes
6363
* [Rename](#rename-field)
6464
* [Skip](#skip-field)
65+
* [Skip if value is evaluated as False](#skip-if-value-is-evaluated-as-false)
6566

6667
### Case conversion
6768

@@ -109,6 +110,26 @@ For complete example, please see [./examples/rename.py](./examples/rename.py)
109110

110111
For complete example, please see [./examples/skip.py](./examples/skip.py)
111112

113+
### Skip if value is evaluated as False
114+
115+
```python
116+
>>> @serialize
117+
... @dataclass
118+
... class World:
119+
... player: str
120+
... enemies: List[str] = field(default_factory=list, metadata={'serde_skip_if_false': True})
121+
122+
>>> world = World('satoshi', ['Rattata', 'Pidgey'])
123+
>>> to_json(world)
124+
'{"player": "satoshi", "enemies": ["Rattata", "Pidgey"]}'
125+
126+
>>> world = World('green', [])
127+
>>> print(to_json(world))
128+
'{"player": "green"}'
129+
```
130+
131+
For complete example, please see [./examples/skip.py](./examples/skip.py)
132+
112133
## Documentation
113134

114135
https://yukinarit.github.io/pyserde/

examples/skip.py

+13
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,26 @@ class Resource:
2222
metadata: Dict[str, str] = field(default_factory=dict, metadata={'serde_skip': True})
2323

2424

25+
@serialize
26+
@dataclass
27+
class World:
28+
player: str
29+
enemies: List[str] = field(default_factory=list, metadata={'serde_skip_if_false': True})
30+
31+
2532
def main():
2633
resources = [
2734
Resource("Stack Overflow", "b6469c3f31653d281bbbfa6f94d60fea130abe38"),
2835
Resource("GitHub", "5cb7a0c47e53854cd00e1a968de5abce1c124601", metadata={"headquarters": "San Francisco"}),
2936
]
3037
print(to_json(resources))
3138

39+
world = World('satoshi', ['Rattata', 'Pidgey'])
40+
print(to_json(world))
41+
42+
world = World('green', [])
43+
print(to_json(world))
44+
3245

3346
if __name__ == '__main__':
3447
main()

serde/de.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class Deserializer(metaclass=abc.ABCMeta):
100100
"""
101101

102102
@abc.abstractclassmethod
103-
def deserialize(self, data, **opts):
103+
def deserialize(cls, data, **opts):
104104
"""
105105
deserialize `data` into an object typically `dict`, `list` or `tuple`.
106106
@@ -149,7 +149,7 @@ def from_obj(c: Type[T], o: Any, de: Type[Deserializer] = None, strict=True, **o
149149
>>>
150150
"""
151151
if de:
152-
o = de().deserialize(o, **opts)
152+
o = de.deserialize(o, **opts)
153153
if o is None:
154154
v = None
155155
if is_deserializable(c):

serde/json.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@
77

88

99
class JsonSerializer(Serializer):
10-
def serialize(self, obj: Any, **opts) -> str:
10+
@classmethod
11+
def serialize(cls, obj: Any, **opts) -> str:
1112
return json.dumps(asdict(obj), **opts)
1213

1314

1415
class JsonDeserializer(Deserializer):
15-
def deserialize(self, s, **opts):
16+
@classmethod
17+
def deserialize(cls, s, **opts):
1618
return json.loads(s, **opts)
1719

1820

19-
def to_json(obj: Any, cls=JsonSerializer) -> str:
20-
return cls().serialize(obj)
21+
def to_json(obj: Any, cls: Type[JsonSerializer] = JsonSerializer) -> str:
22+
return cls.serialize(obj)
2123

2224

2325
def from_json(c: Type[T], s: str, de: Type[Deserializer] = JsonDeserializer, **opts) -> T:

serde/msgpack.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99

1010

1111
class MsgPackSerializer(Serializer):
12-
def serialize(self, obj: Any, **opts) -> str:
12+
@classmethod
13+
def serialize(cls, obj: Any, **opts) -> str:
1314
return msgpack.packb(astuple(obj), **opts)
1415

1516

1617
class MsgPackDeserializer(Deserializer):
17-
def deserialize(self, s, **opts):
18+
@classmethod
19+
def deserialize(cls, s, **opts):
1820
unp = msgpack.unpackb(s, raw=False, use_list=False, **opts)
1921
logger.debug('unpack from msgpack: {unp}')
2022
return unp

serde/se.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Serializer(metaclass=abc.ABCMeta):
2727
"""
2828

2929
@abc.abstractclassmethod
30-
def serialize(self, obj):
30+
def serialize(cls, obj):
3131
pass
3232

3333

@@ -72,7 +72,7 @@ def wrap(cls: Type[T]) -> Type[T]:
7272
setattr(cls, HIDDEN_NAME, Hidden())
7373

7474
def serialize(self, ser, **opts) -> None:
75-
return ser().serialize(self, **opts)
75+
return ser.serialize(self, **opts)
7676

7777
setattr(cls, SE_NAME, serialize)
7878
cls = se_func(cls, TO_ITER, render_astuple(cls))
@@ -185,10 +185,11 @@ class Field:
185185
case: Optional[str] = None
186186
rename: Optional[str] = None
187187
skip: Optional[bool] = None
188+
skip_if_false: Optional[bool] = None
188189

189190
@staticmethod
190191
def from_dataclass(f: DataclassField) -> '':
191-
return Field(f.type, f.name, rename = f.metadata.get('serde_rename'), skip = f.metadata.get('serde_skip'))
192+
return Field(f.type, f.name, rename = f.metadata.get('serde_rename'), skip = f.metadata.get('serde_skip'), skip_if_false = f.metadata.get('serde_skip_if_false'))
192193

193194
@property
194195
def varname(self) -> str:
@@ -248,7 +249,12 @@ def {{func}}(obj):
248249
res = {}
249250
{% for f in cls|fields -%}
250251
{% if not f.skip|default(False) %}
252+
{% if f.skip_if_false|default(False) %}
253+
if {{f|arg|rvalue()}}:
254+
res["{{f|case}}"] = {{f|arg|rvalue()}}
255+
{% else %}
251256
res["{{f|case}}"] = {{f|arg|rvalue()}}
257+
{% endif %}
252258
{% endif %}
253259
{% endfor -%}
254260
return res

serde/toml.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@
99

1010

1111
class TomlSerializer(Serializer):
12-
def serialize(self, obj, **opts) -> str:
12+
@classmethod
13+
def serialize(cls, obj, **opts) -> str:
1314
return toml.dumps(asdict(obj))
1415

1516

1617
class TomlDeserializer(Deserializer):
17-
def deserialize(self, s, **opts):
18+
@classmethod
19+
def deserialize(cls, s, **opts):
1820
return toml.loads(s)
1921

2022

21-
def to_toml(obj, se=TomlSerializer) -> str:
23+
def to_toml(obj, se: Type[TomlSerializer]=TomlSerializer) -> str:
2224
"""
2325
Take an object and return toml string.
2426
@@ -45,7 +47,7 @@ def to_toml(obj, se=TomlSerializer) -> str:
4547
if is_serializable(obj):
4648
return obj.__serde_serialize__(se)
4749
else:
48-
return se().serialize(obj)
50+
return se.serialize(obj)
4951

5052

5153
def from_toml(c: Type[T], s: str, de: Type[Deserializer] = TomlDeserializer) -> T:

serde/yaml.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99

1010

1111
class YamlSerializer(Serializer):
12-
def serialize(self, obj, **opts) -> str:
12+
@classmethod
13+
def serialize(cls, obj, **opts) -> str:
1314
return yaml.safe_dump(asdict(obj), **opts)
1415

1516

1617
class YamlDeserializer(Deserializer):
17-
def deserialize(self, s, **opts):
18+
@classmethod
19+
def deserialize(cls, s, **opts):
1820
return yaml.safe_load(s, **opts)
1921

2022

@@ -39,7 +41,7 @@ def to_yaml(obj, se: Type[Serializer] = YamlSerializer) -> str:
3941
if is_serializable(obj):
4042
return obj.__serde_serialize__(se)
4143
else:
42-
return se().serialize(obj)
44+
return se.serialize(obj)
4345

4446

4547
def from_yaml(c: Type[T], s: str, de: Type[Deserializer] = YamlDeserializer) -> T:

0 commit comments

Comments
 (0)