s-knibbs / dataclasses-jsonschema

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

Order in `Union` field containing `JsonSchemaMixin` matters (i.e. throws for some orders) #180

Closed zuckerruebe closed 2 years ago

zuckerruebe commented 2 years ago

Hi there

The following test passes:

import typing as _tp
import dataclasses as _dc
import dataclasses_jsonschema as _dcj

def testDataClassInUnion(self):
    dataClass = DataClass(Variable("Voldemort"))

    json = dataClass.to_json()

    assert json == """{"values": {"name": "Voldemort"}}"""

@_dc.dataclass
class Variable(_dcj.JsonSchemaMixin):
    name: str

Value = _tp.Union[Variable, float]

@_dc.dataclass
class DataClass(_dcj.JsonSchemaMixin):
    values: Value

Changing the definition of Value slightly by changing around the float and Variable like so:

Value = _tp.Union[float, Variable]

Gives the following error:

tests\trnsysGUI\testSerialization.py:313 (TestSerialization.testDataclassInUnion)
self = <tests.trnsysGUI.testSerialization.TestSerialization object at 0x000001B0F7583EE0>

    def testDataclassInUnion(self):
        dataClass = DataClass(Variable("Voldemort"))

>       json = dataClass.to_json()

testSerialization.py:317: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\venv\lib\site-packages\dataclasses_jsonschema\__init__.py:898: in to_json
    return json.dumps(self.to_dict(omit_none, validate), **json_kwargs)
..\..\..\..\..\AppData\Local\Programs\Python\Python39\lib\json\__init__.py:231: in dumps
    return _default_encoder.encode(obj)
..\..\..\..\..\AppData\Local\Programs\Python\Python39\lib\json\encoder.py:199: in encode
    chunks = self.iterencode(o, _one_shot=True)
..\..\..\..\..\AppData\Local\Programs\Python\Python39\lib\json\encoder.py:257: in iterencode
    return _iterencode(o, 0)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <json.encoder.JSONEncoder object at 0x000001B0F5C98CA0>
o = Variable(name='Voldemort')

    def default(self, o):
        """Implement this method in a subclass such that it returns
        a serializable object for ``o``, or calls the base implementation
        (to raise a ``TypeError``).

        For example, to support arbitrary iterators, you could
        implement default like this::

            def default(self, o):
                try:
                    iterable = iter(o)
                except TypeError:
                    pass
                else:
                    return list(iterable)
                # Let the base class default method raise the TypeError
                return JSONEncoder.default(self, o)

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

..\..\..\..\..\AppData\Local\Programs\Python\Python39\lib\json\encoder.py:179: TypeError