Rahul Patwa
Rahul Patwa

Reputation: 117

Unable to serialize a nested python object using json.dumps()

I am new to python so sorry about the naive questions. I have a simple code snipper where I try to serialize a python object to a dictionary using json.dumps()

import json


class Document:
    uid = "1"
    content = "content1"
    domain = "domain"
    title = "title"


class ASSMSchema:
    requestSource = "unittest"
    documents = []


def entry():
    myObj = ASSMSchema()

    myObj.requestSource = "unittest"

    document1 = Document()
    document1.uid = "1"
    document1.content = "content1"
    document1.domain = "domain"
    document1.title = "title"

    myObj.documents.append(document1)

    print(json.dumps(myObj.__dict__))


if __name__ == "__main__":
    entry()

I get the following output when I run the above code

{"requestSource": "unittest"}

This is not expected however, since it should also seralize the List of "Document" objects. Appreciate your answers. Thanks in advance!

Upvotes: 1

Views: 1910

Answers (1)

Niklas Mohrin
Niklas Mohrin

Reputation: 1918

Your class definition of ASSMSchema defines the class members documents and requestSource. These are not attributes of a single instance of this class, but shared between all instances. When you are running myObj.requestSource = "unittest", you are defining a member variable on the instance myObj. This member is actually reflected in the output of json.dumps, whereas the class members (like documents) are not.

For further reading, see https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables

Depending on the complexity and desired maintainability of your program, there are multiple approaches to archieve your desired behaviour. Firstly, you have to fix the mistake in both class definitions. To define a class with instance variables instead of class variables, do something like this:

class Foo:
    # class variables go here

    def __init__(self, field1, field2):
        # This method is called when you write Foo(field1, field2)

        # these are instance variables
        self.field1 = field1
        self.field2 = field2

If you want to dump this class as JSON, you can simply use the trick with __dict__: print(json.dumps(Foo(1,2).__dict__)) will output something like { "field1": 1, "field2": 2 }.

In your case, there is the documents member though, which is not JSON serializable by default. Therefore, you must handle this separately as well. You could write an encoder for your ASSMSchema (see this thread for more info on that). It could be implemented roughly like this:

from json import JSONEncoder

class ASSMSchemaEncoder(JSONEncoder):
    def default(self, o):
        return {
            "requestSource": o.requestSource,
            # Convert the list of Document objects to a list of dict
            "documents": [d.__dict__ for d in o.documents]
        }

Now, when serializing an instance of ASSMSchema, this implemention is used and the documents member is replaced with a list of dictionaires (which can be serialized by the default encoder). Note, that you have to specify this encoder when calling json.dumps, see the linked thread above.

Upvotes: 2

Related Questions