1

I am trying to decode JSON into python objects. Looked at some of the samples/answers here and other website but can't seem to find the answer.

Here's the code:

import json
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 ADecoder(json.JSONDecoder):
    def decode (self, json_string):
        default_obj = super(A, self).decode(json_string)
        a_obj = A(default_obj['n'], default_obj['a'])
        return a_obj

class BDecoder(json.JSONDecoder):
    def decode (self, json_string):
        default_obj = super(BDecoder, self).decode(json_string)
        #The problem with the code above is that here listOfA is a list of
        #generic python objects.
        b_obj = B(default_obj['b'], [default_obj['listOfA']])
        return b_obj

Here's sample JSON

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

I also tried to see if I can re-convert the listOfA back to string and then calling ADecoder. For example something like this:

aDecoder = ADecoder()
b_obj = B(default_obj['b'], [aDecoder.decode(x.__str__()) for x in default_obj['servers']])

Firstly this didn't work because Python doesn't know how to convert my object to (JSON) string but even if it did the above that would be terribly inefficient.

Any suggestions on how I could solve the problem? Is there a way to inject my ADecoder before Python converts the JSON string to generic objects? Tried looking at the samples, tutorials, etc., can't seem to find this scenario.

3
  • 1
    docs.python.org/2/library/json.html - see the "Specializing JSON object decoding:" example. Commented Aug 5, 2015 at 15:32
  • stackoverflow.com/questions/6578986/… dup? Commented Aug 5, 2015 at 15:38
  • Tom - I did. I am new to Python (4 days) - can't see how to use the examples there for this problem. I have some ideas let me see if it solves this issue. Commented Aug 5, 2015 at 15:38

1 Answer 1

3

You must simply rebuild the listOfA as a list comprehension from the list of maps :

class BDecoder(json.JSONDecoder):
    def decode (self, json_string):
        default_obj = super(BDecoder, self).decode(json_string)
        b_obj = B(default_obj['b'], [A(x['n'], x['a']) for x in default_obj['listOfA'] ] )
        return b_obj

Do not try to re-use ADecoder in BDecoder.


But this method would not be scalable. If you want to be able to use deeply nested object, you must refactor your decoders : passing for a json string to an object composed of lists and maps has do be done only once and is as simple as json.load(file) or json.loads(string). But converting those maps to objects has to be done multiple time : this is the part that must be explicitely coded.

As your example is simple I just used a static method in class A and B for that :

class A:
    def __init__ (self, n, a):
        self.n = n
        self.a = a
    @staticmethod
    def decode(jsonMap):
        return A(jsonMap['n'], jsonMap['a'])

class B:
    def __init__ (self, b, listOfA):
        self.b = b
        self.listOfA = list(listOfA)
    @staticmethod
    def decode(jsonMap):
        return B(jsonMap['b'], [ A.decode(x) for x in jsonMap['listOfA'] ])

That way you can easily go one step further:

class C:
    def __init__ (self, b, listOfB):
        self.c = c
        self.listOfB = list(listOfB)
    @staticmethod
    def decode(jsonMap):
        return C(jsonMap['c'], [ B.decode(x) for x in jsonMap['listOfB'] ])
Sign up to request clarification or add additional context in comments.

3 Comments

Serge - thanks. Request clarification - suppose I have an object with n level deep nested objects, I need to have the creation logic of nth level object in the first level? Is there a way to leverage the respective decoders? A "factory" type pattern? Thanks in advance!
@user3379755 : this method would not be scalable. See my edit for another way
Serge - this worked and made the code a lot cleaner!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.