Reputation: 71
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.
Upvotes: 1
Views: 1712
Reputation: 148965
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'] ])
Upvotes: 3