Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to use Python's JSONDecoder?

For the life of me I haven’t been able to find any example of this for Python.

Say I have a class Person, and I want to serialize and deserialize an object from that class.

class Person:
    def __init__(self, n, b):
        self._name = n
        self._born = b

    @property
    def name(self):
        return self._name

    @property
    def born(self):
        return self._born

    def __str__(self):
        return f"{self.name} was born in {self.born.isoformat()}"

p1 = Person("Baltasar", datetime(1919, 7, 1))

Now, I want to serialize that nice Person object, and I want to support datetime objects. So I wrote a custom serializer:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

class JSONCoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return { "__class__": "datetime",
                     "y": obj.year,
                     "month": obj.month,
                     "d": obj.day,
                     "h": obj.hour,
                     "minute": obj.minute,
                     "s": obj.second }

        return json.JSONEncoder.default(self, obj)

The purpose is to be able to support a couple of types, including datetime.

Now comes the time to deserialize. I had to write a custome deserializer to match the serializer. Here it is:

class JSONDCoder(json.JSONDecoder):
    def __init__(self):
        json.JSONDecoder.__init__(self, object_hook=JSONDCoder.from_dict)

    @staticmethod
    def from_dict(d):
        if d.get("__class__") == "datetime":
            return datetime(d["y"], d["month"], d["d"],
                            d["h"], d["minute"], d["s"])

If I trigger:

coder = JSONCoder()
s = coder.encode(p1.__dict__)
print("Encoded: " + s)

dcoder = JSONDCoder()
d = dcoder.decode(s)
print("Decoded: " + str(d))

I obtain the following output:

Encoded: {"_name": "Baltasar", "_born": {"__class__": "datetime", "y": 1919, "month": 7, "d": 0, "h": 0, "minute": 0, "s": 0}}
Decoded: None
...

The only way to obtain anything apart from None, is to modify the decoder and change it to:

class JSONDCoder(json.JSONDecoder):
    def __init__(self):
        json.JSONDecoder.__init__(self, object_hook=JSONDCoder.from_dict)

    @staticmethod
    def from_dict(d):
        if d.get("__class__") == "datetime":
            return d

Whatever other option makes it to return None, as shown above.

How can I have my custom decoder so it supports datetime types?

>Solution :

You simply forgot to add a return statement at the end of the from_dict method.

class JSONDCoder(json.JSONDecoder):
def __init__(self):
    json.JSONDecoder.__init__(self, object_hook=JSONDCoder.from_dict)

@staticmethod
def from_dict(d):
    if d.get("__class__") == "datetime":
        return datetime(d["y"], d["month"], d["d"],
                        d["h"], d["minute"], d["s"])
    return d

This definition should work, the output for me is the following:

Encoded: {"_name": "Baltasar", "_born": {"__class__": "datetime", "y": 1919, "month": 7, "d": 1, "h": 0, "minute": 0, "s": 0}}
Decoded: {'_name': 'Baltasar', '_born': datetime.datetime(1919, 7, 1, 0, 0)}

Let me know if it was what you were looking for.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading