Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load JSON data into nested classes?

I have JSON data as follows:

{
    "Address": {
        "House_Number": 2,
        "State": "MA",
        "Street_Number": 13
    },
    "Name": "John"
}

I want to load it into a class defined as follows:

class Address:
    def __init__(self):
        self.House_Number = 0

class Employee:
    def __init__(self):
        self.Name = ''
        self.Address = Address()

If I use the class Employee as object_hook, then it uses the same class for both the objects (the outer object having Name and Address as members and inner object having members House_Number etc.).

Basically, if e is the object in which the JSON data has been loaded, then type(e.Address) should be Address not Employee.

Is there any way to load this JSON data into Employee class maintaining the class hierarchy? The hierarchy can be arbitrarily deep.

like image 540
Chandrahas Avatar asked Nov 20 '14 11:11

Chandrahas


People also ask

How to deserialize JSON to custom class objects in python?

To deserialize the string to a class object, you need to write a custom method to construct the object. You can add a static method to ImageLabelCollection inside of which you construct Label objects from the loaded JSON dictionary and then assign them as a list to the class variable bbox.

How do I load a nested json file in Python?

Use pd. read_json() to load simple JSONs and pd. json_normalize() to load nested JSONs. You can easily access values in your JSON file by chaining together the key names and/or indices.


1 Answers

You can identify objects by looking at their keys. You can then map them to the appropriate class.

Using your example data:

class AddressClass:
    # The parameters to init needs to be the same as the json keys
    def __init__(self, House_Number, Street_Number, State):
        self.house_number = House_Number
        self.street_number = Street_Number
        self.state = State

class EmployeeClass:
    # Same here
    def __init__(self, Name, Address):
        self.name = Name
        self.address = Address

# Map keys to classes
mapping = {frozenset(('House_Number', 
                      'Street_Number', 
                      'State')): AddressClass,
           frozenset(('Name', 
                      'Address')): EmployeeClass}

Then, create a function that converts a dictionary to an appropriate python class. This will be passed to json.load as object_hook:

def class_mapper(d):
    return mapping[frozenset(d.keys())](**d)

Above, frozenset is used because dict keys in the json are unordered (hence the set) and the dict keys in the mapping needs to be hashable (hence "frozen").

Finally, parse the json with the class_mapper function as object_hook:

j = '''
{
    "Address": {
        "House_Number": 2,
        "State": "MA",
        "Street_Number": 13
    },
    "Name": "John"
}
'''
employee = json.loads(j, object_hook=class_mapper)
print(employee.name,
      employee.address.house_number,
      employee.address.street_number,
      employee.address.state)
# John 2 13 MA

Additions

If your json data have optional keys, you can create a more robust class_mapper, although it may be slightly slower. You also need to add default values to the class constructor parameters that are optional.

def class_mapper(d):
    for keys, cls in mapping.items():
        if keys.issuperset(d.keys()):
            return cls(**d)
    else:
        # Raise exception instead of silently returning None
        raise ValueError('Unable to find a matching class for object: {!s}'.format(d))
like image 77
parchment Avatar answered Sep 28 '22 03:09

parchment