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.

Anand S Kumar
  • 76,986
  • 16
  • 159
  • 156
user3379755
  • 71
  • 1
  • 7
  • 1
    https://docs.python.org/2/library/json.html - see the "Specializing JSON object decoding:" example. – Tom Dalton Aug 05 '15 at 15:32
  • http://stackoverflow.com/questions/6578986/how-to-convert-json-data-into-a-python-object dup? – FirebladeDan Aug 05 '15 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. – user3379755 Aug 05 '15 at 15:38

1 Answers1

2

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'] ])
Serge Ballesta
  • 121,548
  • 10
  • 94
  • 199
  • 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 Aug 05 '15 at 19:19
  • @user3379755 : this method would not be scalable. See my edit for another way – Serge Ballesta Aug 06 '15 at 05:59
  • Serge - this worked and made the code a lot cleaner! – user3379755 Aug 06 '15 at 21:22