Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Python JSON

JSON

JSON - JavaScript Object Notation

  • json

JSON is basically the data format used by JavaScript. Because its universal availability it became the de-facto standard for data communication between many different languages. Most dynamic languages have an fairly good mapping between JSON and their own data structures. Lists and dictionaries in the case of Python.

Documentation of the Python json library.

Examples:

{"fname": "Foo", "lname": "Bar", "email": null, "children": ["Moo", "Koo", "Roo"], "fixed": ["a", "b"]}

JSON dumps

  • dumps

  • Dictionaries and lists are handles

  • Tuples are indistinguishable from lists

  • Always Double-quotes

  • null instead of None

  • No trailing comma

import json

data = {
  "fname" : 'Foo',
  "lname" : 'Bar',
  "email" : None,
  "children" : [
     "Moo",
     "Koo",
     "Roo",
  ],
  "fixed": ("a", "b"),
}
print(data)

json_str = json.dumps(data)
print(json_str)

with open('data.json', 'w') as fh:
    fh.write(json_str)
{'fname': 'Foo', 'lname': 'Bar', 'email': None, 'children': ['Moo', 'Koo', 'Roo'], 'fixed': ('a', 'b')}
{"fname": "Foo", "lname": "Bar", "email": null, "children": ["Moo", "Koo", "Roo"], "fixed": ["a", "b"]}

dumps can be used to take a Python data structure and generate a string in JSON format. That string can then be saved in a file, inserted in a database, or sent over the wire.

JSON loads

  • loads
import json

with open('data.json') as fh:
    json_str = fh.read()

print(json_str)
data = json.loads(json_str)
print(data)
{"fname": "Foo", "lname": "Bar", "email": null, "children": ["Moo", "Koo", "Roo"], "fixed": ["a", "b"]}
{'fname': 'Foo', 'lname': 'Bar', 'email': None, 'children': ['Moo', 'Koo', 'Roo'], 'fixed': ['a', 'b']}

dump

  • dump
import json

data = {
    "fname" : 'Foo',
    "lname" : 'Bar',
    "email" : None,
    "children" : [
        "Moo",
        "Koo",
        "Roo",
    ],
}

print(data)

with open('data.json', 'w') as fh:
    json.dump(data, fh)

As a special case dump will save the string in a file or in other stream.

load

  • load
import json

with open('data.json', 'r') as fh:
    data = json.load(fh)
print(data)

Round trip

  • loads
  • dumps
import json
import os
import time
import sys

if len(sys.argv) != 2:
    exit("Usage: {sys.argv[0]}  NAME")

data = {
    'name': [],
    'time': [],
}
filename = 'mydata.json'

if os.path.exists(filename):
    with open(filename) as fh:
        json_str = fh.read()
        # print(json_str)
        data = json.loads(json_str)

data['name'].append(sys.argv[1])
data['time'].append(time.time())



with open(filename, 'w') as fh:
   json_str = json.dumps(data, indent=4)
   fh.write(json_str)

Pretty print JSON

import json

data = {
    "name" : "Foo Bar",
    "grades" : [23, 47, 99, 11],
    "children" : {
        "Peti Bar" : {
            "email": "peti@bar.com",
        },
        "Jenny Bar" : {
            "phone": "12345",
        },
    }
}

print(data)
print(json.dumps(data))
print(json.dumps(data, indent=4, separators=(',', ': ')))
{'name': 'Foo Bar', 'grades': [23, 47, 99, 11], 'children': {'Peti Bar': {'email': 'peti@bar.com'}, 'Jenny Bar': {'phone': '12345'}}}
{"name": "Foo Bar", "grades": [23, 47, 99, 11], "children": {"Peti Bar": {"email": "peti@bar.com"}, "Jenny Bar": {"phone": "12345"}}}
{
    "name": "Foo Bar",
    "grades": [
        23,
        47,
        99,
        11
    ],
    "children": {
        "Peti Bar": {
            "email": "peti@bar.com"
        },
        "Jenny Bar": {
            "phone": "12345"
        }
    }
}

Serialize Datetime objects in JSON

Sort keys in JSON

import json

data = {
    "name" : "Foo Bar",
    "grades" : [23, 47, 99, 11],
    "children" : {
        "Peti Bar" : {
            "email": "peti@bar.com",
        },
        "Jenny Bar" : {
            "phone": "12345",
        },
    }
}

print(json.dumps(data, sort_keys=True, indent=4, separators=(',', ': ')))

{
    "children": {
        "Jenny Bar": {
            "phone": "12345"
        },
        "Peti Bar": {
            "email": "peti@bar.com"
        }
    },
    "grades": [
        23,
        47,
        99,
        11
    ],
    "name": "Foo Bar"
}

Set order of keys in JSON - OrderedDict

  • collections
  • OrderedDict
from collections import OrderedDict
import json

d = {}
d['a'] = 1
d['b'] = 2
d['c'] = 3
d['d'] = 4

planned_order = ('b', 'c', 'd', 'a')
e = OrderedDict(sorted(d.items(), key=lambda x: planned_order.index(x[0])))
print(e)

out = json.dumps(e, sort_keys=False, indent=4, separators=(',', ': '))
print(out)

print('-----')

# Create index to value mapping dictionary from a list of values
planned_order = ('b', 'c', 'd', 'a')
plan = dict(zip(planned_order, range(len(planned_order))))
print(plan)

f = OrderedDict(sorted(d.items(), key=lambda x: plan[x[0]]))
print(f)
out = json.dumps(f, sort_keys=False, indent=4, separators=(',', ': '))
print(out)

OrderedDict([('b', 2), ('c', 3), ('d', 4), ('a', 1)])
{
    "b": 2,
    "c": 3,
    "d": 4,
    "a": 1
}
-----
{'b': 0, 'c': 1, 'd': 2, 'a': 3}
OrderedDict([('b', 2), ('c', 3), ('d', 4), ('a', 1)])
{
    "b": 2,
    "c": 3,
    "d": 4,
    "a": 1
}

Exercise: Counter in JSON

Write a script that will provide several counters. The user can provide an argument on the command line and the script will increment and display that counter. Keep the current values of the counters in a single JSON file. The script should behave like this:

$ python counter.py foo
1

$ python counter.py foo
2

$ python counter.py bar
1

$ python counter.py foo
3
  • Extend the exercise so if the user provides the --list flag then all the indexes are listed (and no counting is done).
  • Extend the exercise so if the user provides the --delete foo parameter then the counter foo is removed.

Exercise: Phone book in JSON

Write a script that acts as a phonebook. As "database" use a file in JSON format.

$ python phone.py Foo 123
Foo added

$ python phone.py Bar
Bar is not in the phonebook

$ python phone.py Bar 456
Bar added

$ python phone.py Bar
456

$ python phone.py Foo
123
  • If the user provides Bar 123 save 123 for Bar.
  • If the user provides Bar 456 tell the user Bar already has a phone number.
  • To update a phone-number the user must provide --update Bar 456
  • To remove a name the user must provide --delete Bar
  • To list all the names the user can provide --list

Solution: Counter in JSON

import json
import sys
import os

filename = 'counter.json'

if len(sys.argv) != 2:
    print("Usage: " + sys.argv[0] + " COUNTER")
    exit()

counter = {}

if os.path.exists(filename):
    with open(filename) as fh:
        json_str = fh.read()
        counter = json.loads(json_str)

name = sys.argv[1]
if name in counter:
    counter[name] += 1
else:
    counter[name] = 1

print(counter[name])


with open(filename, 'w') as fh:
    json_str = json.dumps(counter)
    fh.write(json_str)

Solution: Phone book

import sys
import json
import os

def main():
    filename = 'phonebook.json'
    phonebook = {}
    if os.path.exists(filename):
        with open(filename) as fh:
            json_str = fh.read()
            phonebook = json.loads(json_str)

    if len(sys.argv) == 2:
        name = sys.argv[1]
        if name in phonebook:
            print(phonebook[name])
        else:
            print("{} is not in the phonebook".format(name))
        return

    if len(sys.argv) == 3:
        name = sys.argv[1]
        phone = sys.argv[2]
        phonebook[name] = phone
        with open(filename, 'w') as fh:
            json_str = json.dumps(phonebook)
            fh.write(json_str)
        return

    print("Invalid number of parameters")
    print("Usage: {} username [phone]".format(sys.argv[0]))

if __name__ == '__main__':
    main()