3

A websocket connection to SeismicPortal is sending me data about earthquakes packed in a JSON object which I get as a multi-line string, e.g.:

{                                                                                                                                                                                               
    "action": "create",                                                                                                                                                                         
    "data": {                                                                                                                                                                                   
        "geometry": {                                                                                                                                                                           
            "coordinates": [                                                                                                                                                                    
                -95.12,                                                                                                                                                                         
                16.52,                                                                                                                                                                          
                -52.0                                                                                                                                                                           
            ],                                                                                                                                                                                  
            "type": "Point"                                                                                                                                                                     
        },                                                                                                                                                                                      
        "id": "20180303_0000046",                                                                                                                                                               
        "properties": {                                                                                                                                                                         
            "auth": "UNM",                                                                                                                                                                      
            "depth": 52.0,                                                                                                                                                                      
            "evtype": "ke",                                                                                                                                                                     
            "flynn_region": "OAXACA, MEXICO",                                                                                                                                                   
            "lastupdate": "2018-03-03T10:26:00.0Z",                                                                                                                                             
            "lat": 16.52,                                                                                                                                                                       
            "lon": -95.12,                                                                                                                                                                      
            "mag": 4.0,                                                                                                                                                                         
            "magtype": "m",                                                                                                                                                                     
            "source_catalog": "EMSC-RTS",                                                                                                                                                       
            "source_id": "652127",                                                                                                                                                              
            "time": "2018-03-03T07:09:05.0Z",                                                                                                                                                   
            "unid": "20180303_0000046"                                                                                                                                                          
        },                                                                                                                                                                                      
        "type": "Feature"                                                                                                                                                                       
    }                                                                                                                                                                                           
}

I want to have the data from the string converted to a python object.

As you see in the JSON data, there is a lot of nesting. As I was defining the classes and their embeddedness to build a on object of a structure which would hold all the data from the JSON I was thinking maybe there is some magic Python function jsonStringToObject which would tailor a class and all subclasses needed to hold all the data in the JSON and make an instance of it.

Let's have the raw JSON string in the variable rawData:

rawData = """{"action":"create","data":{"geometry": {"type": "Point","coordinates": [... """

Right now I have to do this:

>>> import json
>>> quake = json.loads(rawData)
>>> quake['data']['properties']['flynn_region']
"OXACA_MEXICO"

but the syntax is crammed with brackets and apostrophes.

I wish I could just access the data like this:

>>> import json    
>>> quake = jsonStringToObject(rawData)
>>> quake.data.properties.flynn_region
"OXACA_MEXICO"
fleetingbytes
  • 2,512
  • 5
  • 16
  • 27
  • 3
    JSON contains lists, strings, integers and similar simple objects. It does not support defining something to be an instance of your own custom class. So, since the format does not support custom classes, the JSON reader cannot support them either. You have to have your own function which converts a dict for instance into your class (after parsing a JSON into a dict or whatever). – zvone Mar 03 '18 at 13:52
  • It seems this question has already been asked https://stackoverflow.com/questions/6578986/how-to-convert-json-data-into-a-python-object – IgSaf Mar 03 '18 at 14:12
  • `json.loads` *does* give you a Python object. – Stefan Pochmann Mar 03 '18 at 14:26
  • 1
    @IgSaf I have seen the seemingly "duplicate" question and I have tried the solution with the named dictionary with the inexplicable and magical 'X' in it. It does get me a nested dictionary structure. The answer provided by Božo Stojković, however, is exactly what I was looking for. Thank you, Božko! – fleetingbytes Mar 03 '18 at 15:08

1 Answers1

13

You could create your own class for that. Use __getitem__, and __setitem__ to get and update values from the object's __dict__ using dot notation:

import json

class PyJSON(object):
    def __init__(self, d):
        if type(d) is str:
            d = json.loads(d)
        self.convert_json(d)

    def convert_json(self, d):
        self.__dict__ = {}
        for key, value in d.items():
            if type(value) is dict:
                value = PyJSON(value)
            self.__dict__[key] = value

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, key):
        return self.__dict__[key]

rawData = """... raw data ..."""

quake = PyJSON(rawData)

Works as expected:

>>> quake.data.properties.flynn_region
'OAXACA, MEXICO'

EDIT: Added to_dict and overridden __repr__ so it's easier to peek at values in console. Renamed convert_json to from_dict.

import json

class PyJSON(object):
    def __init__(self, d):
        if type(d) is str:
            d = json.loads(d)

        self.from_dict(d)

    def from_dict(self, d):
        self.__dict__ = {}
        for key, value in d.items():
            if type(value) is dict:
                value = PyJSON(value)
            self.__dict__[key] = value

    def to_dict(self):
        d = {}
        for key, value in self.__dict__.items():
            if type(value) is PyJSON:
                value = value.to_dict()
            d[key] = value
        return d

    def __repr__(self):
        return str(self.to_dict())

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, key):
        return self.__dict__[key]

rawData = """... raw data ..."""

quake = PyJSON(rawData)

Before:

>>> quake.data.geometry
<__main__.PyJSON object at 0xADDRESS>

After:

>>> quake.data.geometry
{'coordinates': [-95.12, 16.52, -52.0], 'type': 'Point'}
Božo Stojković
  • 2,893
  • 1
  • 27
  • 52