dataclasses-json icon indicating copy to clipboard operation
dataclasses-json copied to clipboard

Efficient way to skip encoding null optionals?

Open deinspanjer opened this issue 4 years ago • 9 comments

Is there a way to configure a dataclass_json class to skip encoding some or any fields when they have None values?

deinspanjer avatar Mar 06 '20 00:03 deinspanjer

I'm also looking for something similar. Apparently this might help https://github.com/marshmallow-code/marshmallow/issues/229

eblis avatar Mar 26 '20 22:03 eblis

In my case I don't use marshmallow and I'm just interested in dropping None values when encoding with to_json function

gastlich avatar May 09 '20 13:05 gastlich

Same Here Actually, I would prefer any optional value which is also it's "default" value be skipped.

If i have an int whose default is 55, it would be good to be able to NOT serialise it if the int is 55, and only serialize it in all other cases.

stevenj avatar May 16 '20 12:05 stevenj

Yes it is easy to skip Null optionals, as i just discovered:

    my_field: Optional[str] = field(
        default=None, metadata=config(exclude=ExcludeIfNone)
    )

exclude takes a function which is passed a single variable, the value of the field. To exclude it needs to return True. SO, to exclude if None, it needs to say:

def ExcludeIfNone(value):
    return value is None

you could also do it inline with a lambda, but i prefer the self documenting nature of exclude=ExcludeIfNone

it would be really nice, if a second parameter could be passed to this function, the field definition. Then you would be able to do a check if the field is its default, as it stands you need to explicitly have a ExcludeIfZero etc, for all the cases of your defaults that you want to not encode.

stevenj avatar Jun 20 '20 08:06 stevenj

~~Thanks @stevenj - thanks for a working solution... but it's a ton of boilerplate?~~

I spoke too soon - I can't seem to get this working --?


@lidatong would you consider a PR to make this an option on the decorator?

Thanks, Chad

chadbr avatar Oct 29 '20 18:10 chadbr

@chadbr I just tested and it worked for me. In my case I used a lambda:

my_field: Optional[dict] = field(default=None, metadata=config(exclude=lambda x: x is None))

geonnave avatar Nov 09 '20 17:11 geonnave

I would love it if the exclude function was also passed the fields metadata in addition to its value. Then one could write a "Exclude if Default" which can't be done now except explicitly as in Default is None, so exclude if None.

stevenj avatar Dec 29 '20 14:12 stevenj

I'm also having this issue with an inconsistent behavior between Marshmallow and dataclass_json. A lot of my data code looks like this (working example):

from dataclasses import dataclass, field
from dataclasses_json import config, dataclass_json
from typing import Optional
from enum import Enum


class ArgType(Enum):
    Type1 = 'type1'
    Type2 = 'type2'


@dataclass_json
@dataclass
class Args:
    id: str
    arg_type: ArgType
    optional: Optional[bool] = field(default=None, metadata=config(exclude=lambda f: f is None))

And the serialization behaves differently between the method used, without common ground, which is disturbing:

>>> a = Args('1', ArgType.Type1)
>>> a.to_dict()
{'id': '1', 'arg_type': <ArgType.Type1: 'type1'>}
>>> a.schema().dump(a)
{'optional': None, 'id': '1', 'arg_type': 'type1'}

The optional field is well excuded in the to_dict version, but here the Enum is not serialized, so it fails in other libraries using the default json Encoder:

self = <json.encoder.JSONEncoder object at 0x111259af0>
o = <ArgType.Type1: 'type1'>

[...redacted]

>       raise TypeError(f'Object of type {o.__class__.__name__} '
                        f'is not JSON serializable')
E       TypeError: Object of type ArgType is not JSON serializable

So, it's either the field metadata specifications are followed but using a data structure that requires extra config everywhere it's used, or the field metadata are ignored and the data structure is correctly serialized.

It can be seen in the marshmallow schema:

>>> schema = a.schema()
>>> schema.fields['optional'].metadata
{}

whisust avatar Jan 26 '21 20:01 whisust

This works for the mixin as well:

class Mixin(DataClassJsonMixin):
    dataclass_json_config = dataclasses_json.config(  # type: ignore
        letter_case=dataclasses_json.LetterCase.CAMEL,  # type: ignore
        undefined=dataclasses_json.Undefined.EXCLUDE,
        exclude=lambda f: f is None # type: ignore
    )["dataclasses_json"]

gegnew avatar Sep 15 '21 12:09 gegnew