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"
4
  • 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). Commented Mar 3, 2018 at 13:52
  • It seems this question has already been asked stackoverflow.com/questions/6578986/… Commented Mar 3, 2018 at 14:12
  • json.loads does give you a Python object. Commented Mar 3, 2018 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! Commented Mar 3, 2018 at 15:08

1 Answer 1

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'}
Sign up to request clarification or add additional context in comments.

2 Comments

Where'd k/v come from in convert_json?
I don't remember how that happened, but from the edits, it seems I wanted to make it more readable, but forgot to update all variable names. Fixed now. Thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.