s-knibbs / dataclasses-jsonschema

JSON schema generation from dataclasses
MIT License
166 stars 38 forks source link

Handling of Optional[...] = None - dataclasses.asdict() vs .to_dict() ...support for exported none? #141

Closed richard-moss closed 3 years ago

richard-moss commented 3 years ago

I just found the difference in handling of dataclasses when you have an element annotated with Optional and a default of None:

from dataclasses import dataclass, asdict
from dataclasses_jsonschema import JsonSchemaMixin
import jsonschema

from typing import Optional, Union

@dataclass
class A(JsonSchemaMixin):
    a: Optional[str] = None

print('schema:\n', json.dumps(A.json_schema(), indent=2))

a = A()

print('to_dict:\n', a.to_dict())
print('asdict:\n', asdict(a))

jsonschema.validate(a.to_dict(), A.json_schema())
jsonschema.validate(asdict(a), A.json_schema())

Using dataclass.asdict() to serialize a will fail validation, as the schema only defines a as a string, and asdict() returns it as None.

However by the dataclass defintion, None is a valid type.

to_dict() gets around this by removing the variable when it's serialized.

Problem

I've got a set of tests that produce a lot of nested results, many of which I've currently got set to a definition of Optional[...] = None.

They fail validation at present against the schema because I'm using dataclasses.asdict() to serialize them.

I could change my serialization to to_dict(), but some of the downstream code might need changing because it then won't receive a value, while today it receives None.

Question

Is there a way to support producing a schema for Optional[..] = None's, where the schema definition allows for none, so that serializing a None to 'none` would still pass validation?

s-knibbs commented 3 years ago

You can use the Nullable type here to allow None to be supplied as a value. So your example would become:


from dataclasses import dataclass, asdict
from dataclasses_jsonschema import JsonSchemaMixin
from dataclasses_jsonschema.type_defs import Nullable
import jsonschema

from typing import Optional, Union

@dataclass
class A(JsonSchemaMixin):
    a: Nullable[Optional[str]] = None

print('schema:\n', json.dumps(A.json_schema(), indent=2))

a = A()

print('to_dict:\n', a.to_dict())
print('asdict:\n', asdict(a))

jsonschema.validate(a.to_dict(), A.json_schema())
jsonschema.validate(asdict(a), A.json_schema())

At some point I need to get round to writing some proper documentation for this package.