Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python custom JSON encoder/decoder not working as expected

Tags:

python

json

I am trying to encode/decode nested objects and I have the list of nested objects as a string instead of JSON objects.

class A:
    def __init__ (self, n, a):
        self.n = n
        self.a = a

class B:
    def __init__ (self, b, listOfA):
        self.b = b
        self.listOfA = []
        for a in listOfA:
            self.listOfA.append(a)

class AEncoder:
    def default (self, obj):
        if isinstance (obj, A):
            return {
                'n' : obj.n
                'a' : obj.a
            }
        return json.JSONEncoder.default(self, obj)

class BEncoder:
    def default (self, obj):
        if isinstance (obj, B):
            return {
                'b' : obj.n
                'listOfA' : json.dumps(obj.listOfA, cls=AEncoder)
            }
        return json.JSONEncoder.default(self, obj)

listOfA = [A('n1', 'a1'), A('n2', 'a2')]
tmpB = B('b', listOfA)

For object A it is working correctly as it is fairly straight forward. The output for B I get is something like this:

{
    "b" : "b"
    "listOfA" : "[{\"n\" : \"n1\", \"a\" : \"a1\"}, {\"n\" : \"n2\", \"a\" : \"a2\"}]"
}

Any ideas where I am wrong? The output should be like this:

{
    "b" : "b"
    "listOfA" : [{"n" : "n1", "a" : "a1"}, {"n" : "n2", "a" : "a2"}]
}
like image 681
user3379755 Avatar asked May 27 '26 13:05

user3379755


1 Answers

You had a few typos in your code, missing commas in dictionaries and you refer to obj.n when you meant obj.b in BEncoder.

Aside from that, the problem is that you are using json.dumps() in the BEncoder. This method is returning a properly-formatted json string. The BEncoder then sees this string and thinks you want the value of listOfA to be a string, which should then be encoded properly for json, thus the character escaping you're seeing.

Here is a solution, that instead of returning a string, returns a "serializable object" (e.g., a list, dict, etc.), as needed by the encoder. It is building a list (which is serializable) out of the dictionaries (serializable) returned by AEncoder().default().

import json
from json import JSONEncoder

class A:
    def __init__ (self, n, a):
        self.n = n
        self.a = a

class B:
    def __init__ (self, b, listOfA):
        self.b = b
        self.listOfA = []
        for a in listOfA:
            self.listOfA.append(a)

class AEncoder(JSONEncoder):
    def default (self, obj):
        if isinstance (obj, A):
            return { 'n' : obj.n, 'a' : obj.a }
        return json.JSONEncoder.default(self, obj)

class BEncoder(JSONEncoder):
    def default (self, obj):
        if isinstance (obj, B):
            a = AEncoder()
            return { 'b' : obj.b, 'listOfA' : [a.default(x) for x in obj.listOfA] }
        return json.JSONEncoder.default(self, obj)

listOfA = [A('n1', 'a1'), A('n2', 'a2')]
tmpB = B('b', listOfA)
print(json.dumps(tmpB, cls=BEncoder))

The output:

{"b": "b", "listOfA": [{"a": "a1", "n": "n1"}, {"a": "a2", "n": "n2"}]}
like image 138
drootang Avatar answered May 30 '26 04:05

drootang



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!