How do I read JSON into a Python Class?
Asked by no_longer_in_sudoers_file:

I have a JSON configuration file that looks like this:

```
{    "container":{        "image":"reporting_engine",        "name":"reporting_engine",        "version":"0.01",        "network":{            "host":{                "ip":"127.0.1.1",                "port":"5000"            },            "container":{                "ip":"127.0.1.1",                "port":"5000"            }        },        "storage":{            "host":"/home/scaldwell/rpe/data",            "container":"/opt/app/data"        }    }}
```

I want to read this JSON configuration file into a Python object so I can address properties in an intuitive way programmatically.

I realize I could do something like this:

```
import jsonwith open(config_file,'r') as f:    config=json.loads(f.read())
```

and then use my configuration parameters like this:

```
print config["network"]["host"]["ip"]
```

While that is acceptable Python, it's about as elegant as a Trump speech and possibly just as likely to have errors since there all of those open and closing brackets and quotes.

I would rather a solution that allows me to do the same print like this:

```
print config.network.host.ip
```

...like a civilized code monkey. :wink:  
[](https://community.atlassian.com/t5/Crowd-questions/How-do-I-read-JSON-into-a-Python-Class/qaq-p/176763?attachment-id=12889){#link_11597467528757}  
[](/tnckb94959/attachments/tnckb94959/crowd-questions/4492/1/image2016-8-13%2011%3A57%3A48.png){#display_11597467528757_0}


Answers:
author: no_longer_in_sudoers_file
body: This code works on my current personal project. It allows me to use config.py to read JSON config files from a shell script that launches and manages my docker containers while also using that same config.py from other python scripts (for the containerized application) to read config properties.

Config files are like Highlander, IMHO. In the end there should be only one!

Here is my solution (config.py):

```
#!/usr/bin/env pythonimport osimport sysimport timeimport jsonimport inspectfrom argparse import ArgumentParserdef parse_args():    parser=ArgumentParser(        description="read/write json config file."    )    parser.add_argument(        "--debug",        action="store_true",        default=False,        help="Determines whether verbose log messages should be printed."    )    parser.add_argument(        "--read",        action="store_true",        default=False,        help="Read the file (--config) and return a key (requires --key)."    )    parser.add_argument(        "--write",        action="store_true",        default=False,        help="Write a key (--key) value (--value) pair to --config"    )    parser.add_argument(        "--key",        dest="key",        type=str,        help="Key name to be read/written"    )    parser.add_argument(        "--value",        dest="value",        type=str,        help="Data value to be written."    )    parser.add_argument(        "--config",        dest="config",        default="app.json",        type=str,        help="The configuration file to be read/written."    )    a=parser.parse_args()    if a.read or a.write:        if a.read and a.write:            print "You cannot use both --read and --write concurrently"            sys.exit(1)        else:            return a    else:           print "You must specify either --read or --write"        sys.exit(1)class Config:       def __init__(self,oName,is_file=False,i=0,debug_mode=False):        self.debug_mode=debug_mode        self.i=i        self.type=None        if is_file:            self.oName="root"            self.type="root"        else:            self.oName=oName            self.type="child"        self.debug("init {}[{}] starting".format(self.oName,self.type))        if is_file:            j=None            try:                with open(oName,'r') as f:                    j=json.loads(f.read())                self.debug(                    "----Raw Input----\n\n{}\n".format(                        json.dumps(j,indent=4)                    )                )                self.deserialize(j,i+1)            except Exception as e:                self.debug("Could not load config file.  Error:{}".format(e))                sys.exit(1)                    self.debug("init {}[{}] complete".format(self.oName,self.type))    def deserialize(self,j,i=0):        self.i=i        try:            self.debug(" ")            self.debug("---deserializer loop started")            self.debug("+&gt;json:{}".format(j))            for k,v in j.viewitems():                self.debug("calling walk() with '{}'".format(k))                self.walk(k,v,i+1)                self.debug("returned from walk() '{}'".format(k))            self.debug("---deserializer loop complete.")        except Exception as e:            self.debug("Could not deserialize config. Error:".format(e))            sys.exit(1)        self.i-=1    def walk(self,key_name,key_value,i=1):        self.i=i        self.debug("walking the config:{}".format(i))        try:            o=None            if type(key_value) is dict:                o=Config(oName=key_name,i=i)                o.deserialize(key_value,i+1)                self.debug("walk(): recovered from recursive call.")                assert key_name != "oName", "oName is reserved for parser"                setattr(self,key_name,o)            else:                self.debug("Set value:{},{}".format(key_name,key_value))                assert key_name != "oName", "oName is reserved for parser"                setattr(self,key_name,key_value)        except Exception as e:            self.debug("config.walk() Error:{}".format(e))            sys.exit(1)        self.i-=1    def dump_config(self,i=0,data_only=False):        old_debug_mode=self.debug_mode        self.debug_mode=True        self.i=i        if i == 0:            c="*"        else:            c="-"        self.debug("{0}DUMP CONFIG START ({1}:{2}){0}".format(c*3,self.type,self.oName))        for k in dir(self):            if data_only and \               not k.startswith("_") and \               not callable(getattr(self,k)):                self.debug("{0:15}:{1}".format(k,getattr(self,k)))            if isinstance(getattr(self,k),Config):                self.i+=1                o=getattr(self,k)                o.dump_config(self.i+1,data_only)                self.i-=1        self.debug("{0}DUMP CONFIG ENDS ({1}:{2}){0}".format(c*3,self.type,self.oName))        self.debug_mode=old_debug_mode    def debug(self,m):        try:            if self.debug_mode:                print "{0:15}|{1:&lt;2}| {2} [{3}]:{4}".format(                    int(time.time()*10000000000),                    hex(self.i),                    " . "*self.i,                    self.oName,                    m                )        except Exception as e:            print "Could not print message: Error:{}".format(e)            sys.exit(1)    def getChildAttribute(self,index,object,path):        self.i+=1        assert isinstance(self,Config),"self must be &lt;instance&gt;"        assert type(index) is int, "index must be &lt;int&gt;"        assert isinstance(object,Config),"object must be &lt;instance&gt;"        assert type(path) is list, "path must be &lt;list&gt;"        for i in path:             assert type(i) in [str,unicode], "path elements must be &lt;str&gt;"        assert index &gt;= 0, "index must be &gt; 0"        assert len(path) &gt; 0, "len(path &lt;list&gt;) must be &gt; 0"        self.debug("position[{}]:{}".format(index,path[index]))        o=getattr(object,path[index])        if (index &lt; len(path)) and isinstance(o,Config):            return self.getChildAttribute(index+1,o,path)        else:            return o        self.i-=1    def read_value(self,k):        if self.debug_mode:self.dump_config(data_only=True)        self.i=0        try:            path=k.split('.')            self.debug("path: {}".format(path))            o=getattr(self,path[0])            self.debug("o:{}:{}".format(o,o.oName))            return 0, self.getChildAttribute(                                    index=1,                                    object=o,                                    path=path            )        except Exception as e:            return 1,e    def write_value(self,k,v):        try:            assert k is not None, "Expected a key string (--key)"            assert v is not None, "Expected a value string (--value)"            assert type(k) is str, "--key must specify a string."            assert type(v) is str, "--value must specify a string."        except Exception as e:            print "Error: {}".format(e)             sys.exit(1)        try:            print "write_value() not implemented yet."        except Exception as e:            print "write_value() Error: {}".format(e)            sys.exit(1)def main():    args=parse_args()    config=Config(args.config,is_file=True,debug_mode=args.debug)    if args.read:        e,v=config.read_value(args.key)        if args.debug:            print "Key:'{}', Value:'{}', ExitCode:{}".format(args.key,v,e)        else:            print v        sys.exit(e)    elif args.write:        sys.exit(config.write_value(args.key,args.value))    else:        print "Uncaught input parameter error.  Expected --read or --write"        sys.exit(1)if __name__ == "__main__":    main()
```

This allows me to have a config file like this:

```
{    "container":{        "image":"reporting_engine",        "name":"reporting_engine",        "ip":"127.0.1.1",        "port":"5000",        "volume":"/storage"    },    "app":{    }}
```

..and to then use the utility to fetch JSON config elements like this:

```
python common/config --read --config reporting_engine/app.json --key container.image
```

Which will write the following to stdout:

```
reporting_engine
```

As the JSON config changes, the code doesn't. That's my favorite kind of software (the flexible kind). :wink:

comments: author: Steven F Behnke
body: image2016-8-13 11:57:48.png


Tags: python