Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialize multiple classes to JSON

I have a class A where it stores a collection of variables of type B, how can I serialize class A to JSON properly?

example:

class A:
    def __init__(self):
        self.b_collection = []
    #...

class B:
    def __init__(self):
        # ...
        pass

and add instances of B into the collection:

a = A()
a.b_collection = [B(), B(), B()]

when I try to serialize a with json.dumps(a) I get this error: Object of type A is not JSON serializable.

Is there a way to specify how to encoder should encode that class?

something like

def __encode__(self, encoder):
    encoder.start_obj()
    encoder.add_property('name', self.value)
    encoder.add_property('age', self.age)
    encoder.end_obj()

which would return something like

{
   name: 'Tomer',
   age: '19'
}
like image 736
Tomergt45 Avatar asked Apr 10 '26 21:04

Tomergt45


1 Answers

You can extend json.JSONEncoder to define how to serialize your objects. The default method of your subclass will take a Python object as an argument. You can return a new object that is (hopefully) encodable, or pass the object on to the parent in hopes that it knows how to encode the object.

For example,

class A:
    def __init__(self):
        self.b_collection = []

class B:
    def __init__(self, name, age):
        self.name = name
        self.age = age


class ABEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, A):
            return {'__A__': obj.b_collection}
        elif isinstance(obj, B):
            return {'__B__': obj.__dict__}
        return super().default(obj)


a = A()
a.b_collection.append(B("Tomer", "19"))
a.b_collection.append(B("Bob", "21"))
a.b_collection.append(B("Alice", "23"))
print(json.dumps(a, cls=ABEncoder, indent=4))

would produce

{
    "__A__": [
        {
            "__B__": {
                "name": "Tomer",
                "age": "19"
            }
        },
        {
            "__B__": {
                "name": "Bob",
                "age": "21"
            }
        },
        {
            "__B__": {
                "name": "Alice",
                "age": "23"
            }
        }
    ]
}

Note that you can handle A and B separately; you don't have to first encode the B objects before returning the encodable form of A; the B objects will be encoded later when the list itself is encoded.

The extra objects make it easier to write a decoder; you don't have to make it this complicated if you don't want to be able to decode the JSON to an instance of A. Instead, you can just define

class ABEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, A):
            return obj.b_collection
        elif isinstance(obj, B):
            return obj.__dict__
        return super().default(obj)

to get

[
    {
        "name": "Tomer",
        "age": "19"
    },
    {
        "name": "Bob",
        "age": "21"
    },
    {
        "name": "Alice",
        "age": "23"
    }
]
    
like image 144
chepner Avatar answered Apr 12 '26 11:04

chepner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!