JavaScript Object Notation, or JSON, is something that started popping up more and more the moment I started doing (Net)DevOps. These are some of my notes and examples.
JSON overview
JSON is an open standard format that can be used to store or transmit data. The two data structures available in JSON are:
- Objects: unordered collection of one or more key-value pairs enclosed in braces {..}.
- Arrays: ordered collection of values enclosed in brackets [..].
An object is similar to a Python dictionary and an array is similar to a Python list.
The values that you can use or come across in these data structures are the following:
- String (0 or more Unicode characters)
- Number (integer or float)
- true
- false
- null
- Object
- Array
JSON basics in Python
There are multiple options available, but the only module I ever used is the one that is found in the Python Standard Library simply called json. The following Python 3.6 examples all use this library.
Dump a Python dictionary as JSON in a file
In the following example script, we use dump to serialize a dictionary to be stored as JSON in a file:
#!/usr/bin/python3 from json import dump d = { 'Automate the Boring Stuff with Python' : 'Al Sweigart', 'Fluent Python' : 'Luciano Ramalho', 'Learning Python' : 'Mark Lutz', 'Python Tricks' : 'Dan Bader', } with open('/var/tmp/dictionary.json', 'w') as f: dump(d, f)
Whenever we translate Python to JSON, we do so using the following conversion table:
Python data structures | serializes to JSON |
---|---|
dict | object |
list, tuple | array |
str | string |
int, float | number |
True | true |
False | false |
None | null |
Note: any file opened using with is automatically closed when the script is done using the file.
Load JSON as a Python dictionary from a file
In the next example script, we turn to load to turn a file with JSON data into a Python dictionary:
#!/usr/bin/python3 from json import load with open('/var/tmp/dictionary.json', 'r') as f: d = load(f)
Whenever we load JSON in a Python script, the following conversion applies:
JSON value | deserializes to Python |
---|---|
object | dict |
array | list |
string | str |
number | int, float |
true | True |
false | False |
null | None |
Emit JSON as string
Instead of using dump to store JSON data in a file, we can turn to dumps and serialize an object to a string:
#!/usr/bin/python3 from json import load, dumps with open('/var/tmp/dictionary.json', 'r') as f: d = load(f) s = dumps(d) print(s)
JSON kwargs to make the output prettier
We can use the indent and separators keyword to increase the readability of any JSON we are working with. The keywords can be passed to the dump as well as the dumps method:
#!/usr/bin/python3 from json import dumps d = { "JSON": [ 'is case-sensitive', 'does not care about whitespaces', 'does not offer a way to put in comments', 'is also valid YAML', ], } # Print the original: print(dumps(d)) # Print the pretty output: print(dumps(d, indent=4, separators=(',', ': ')))
When we run the script, you can see that the readability of the JSON is drastically improved. The first line in the output is the ‘raw’ JSON and after this, you can see how the same JSON is printed when the keyword arguments indent and separators are used:
{"JSON": ["is case-sensitive", "does not care about whitespaces", "does not offer a way to put in comments", "is also valid YAML"]} { "JSON": [ "is case-sensitive", "does not care about whitespaces", "does not offer a way to put in comments", "is also valid YAML" ] }
JSON kwargs to sort the output:
Setting sort_keys to True will output dictionaries with their keys sorted:
#!/usr/bin/python3 from json import dumps d = { "dict z": {"b": "b", "a": "a", "c": "c"}, "dict b": {"d": "d", "h": "h"}, "dict a": {"k": "k", "a": "a"}, "dict c": [ 'c', 'b', 'a', ] } # Sort it by keys and print it: print(dumps(d, sort_keys=True, indent=4, separators=(',', ': ')))
This will output the following:
{ "dict a": { "a": "a", "k": "k" }, "dict b": { "d": "d", "h": "h" }, "dict c": [ "c", "b", "a" ], "dict z": { "a": "a", "b": "b", "c": "c" } }
The dictionaries were sorted, the list that was present in of the dictionaries was not.
Same as with indent and separators, this works for dump as well as dumps.
Dictify a web page
In this example script, we use urlopen from urllib.request to open a URL. We read the return and load it using json.loads:
#!/usr/bin/python3 import json from pprint import pprint from urllib.request import urlopen # Open the web page: html_content = urlopen('http://validate.jsontest.com/?json=%5BJSON-code-to-validate%5D').read() # Load the JSON: d_json_test = json.loads(html_content) # Print the JSON to screen: pprint(d_json_test) # All in one line: pprint(json.loads(urlopen('http://validate.jsontest.com/?json=%5BJSON-code-to-validate%5D').read()))
If we run the script, the last line outputs the following:
{'empty': False, 'object_or_array': 'array', 'parse_time_nanoseconds': 29928, 'size': 1, 'validate': True}
Using JSON in jinja
In this last example, we use json.loads to deserialize JSON from a string, and then use the resulting dictionary in a Jinja template:
#!/usr/bin/python3 import json from jinja2 import Template json_str = ''' [ {"hostname": "ny01", "mgmt-ip": "10.0.0.1" }, {"hostname": "ny02", "mgmt-ip": "10.0.0.2" }, {"hostname": "ny03", "mgmt-ip": "10.0.0.3" }, {"hostname": "ny04", "mgmt-ip": "10.0.0.4" } ] ''' # Load json from string: routers = json.loads(json_str) # Define template: template = Template(''' {% for router in routers %} set system host-name {{ router['hostname'] }} set interfaces lo0 unit 0 family inet address {{ router['mgmt-ip'] }} primary {% endfor %} ''') # Render template and send the output to screen: print(template.render(routers = routers))
Running the previous script will output the following:
set system host-name ny01 set interfaces lo0 unit 0 family inet address 10.0.0.1 primary set system host-name ny02 set interfaces lo0 unit 0 family inet address 10.0.0.2 primary set system host-name ny03 set interfaces lo0 unit 0 family inet address 10.0.0.3 primary set system host-name ny04 set interfaces lo0 unit 0 family inet address 10.0.0.4 primary