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

feat: use default value when a not optional field is null from json.

Open Bidaya0 opened this issue 1 year ago • 5 comments

Case 1: The upstream has passed a value that may be empty. This empty value has no business meaning. It is hoped that the dataclass_json layer can be completed instead of introducing more types to reduce the complexity of subsequent business logic.

Case 1: The upstream has passed a value that may be empty. This empty value has no business meaning. It is hoped that the dataclass_json layer can be completed instead of introducing more types to reduce the complexity of subsequent business logic.

{
  "a": null
}

{
  "a": ["1,2,3"]
}
a: Tuple[str] = ()

then with always get the type as Tuple[str].

Bidaya0 avatar Jun 04 '23 14:06 Bidaya0

@Bidaya0 I'm not sure I understand your problem. Could you please come up with a code sample of the behaviour you want vs the behaviour you see when using the library?

george-zubrienko avatar Jun 05 '23 18:06 george-zubrienko

@dataclass_json
class A:
 a =  Tuple[str] = ()

Case 1 input json:

{
  "a": null
}

library output:

obj = A().from_json()
print(obj.a) #  None here

except :

obj = A().from_json()
print(obj.a) #  empty Tuple () here

@george-zubrienko

Bidaya0 avatar Jun 06 '23 03:06 Bidaya0

Alright I see what you want to do, and I would actually like to do some work on that, but in the opposite direction of what you want :)

In your case, library should throw an exception, as null and Tuple are different types and assigning null fields to an empty list is a semantic error and should be treated as such. But what you want should be achievable with post_init. So, tldr:

@dataclass_json
class A1:
    a: Tuple[str] = ()

@dataclass_json
class A2:
    a: Optional[Tuple[str]] = None

@dataclass_json
class A3:
    a: Optional[Tuple[str]] = None

    def __post_init__(self):
        self.a = () if not self.a else self.a

data_str = '{"a": null }'
data_obj1 = A1.from_json(data_str) # FormatException: Received `null` in the non-nullable field

data_obj2 = A2.from_json(data_str) # A2(a: None)

data_obj3 = A3.from_json(data_str) # A3(a: ())

I'll create an issue to enforce correct type semantics as a follow-up.

george-zubrienko avatar Jun 06 '23 10:06 george-zubrienko

@george-zubrienko

Ok, I kind of misunderstood this part of the docs. Although some means are really provided here to fill in the value, but for typing, it is still either verified or not verified at all.

Person.from_json('{"name": 42}')  # This is ok. 42 is not a `str`, but
                                  # dataclass creation does not validate types
Person.schema().loads('{"name": 42}')  # Error! Raises `ValidationError`

btw, perhaps I should define the deserialization work of these in the field provided by marshmallow and keep the dataclass tidy.

Thanks for answering.

Bidaya0 avatar Jun 06 '23 12:06 Bidaya0

btw, perhaps I should define the deserialization work of these in the field provided by marshmallow and keep the dataclass tidy.

I think it is a good idea, and thank you as well for raising the issue, as we know now there is some improvement we should work on :)

george-zubrienko avatar Jun 06 '23 13:06 george-zubrienko