json

JSON logo

Par Yoan Blanc 1

Introduction

JSON (JavaScript Object Notation) est un format simple, compact qui a, au fil des ans, remplacé XML comme format d’échange préféré. Standardisé au sein de l’Ecma [Bra14], il est devenu incontournable dans les systèmes actuels.

JSON comporte six types : chaîne de caractères, nombre, objet, tableau, booléen (true, false) et null.

{
    "clé": "valeur",
    "age": 20,
    "pi": 314.59e-2,
    "tableau": [1, 2, 3, ["x", "y"]],
    "objet": {
        "clé": "autre valeur",
        "booléen": false
    },
    "rien": null
}

Il serait possible de lire directement cette structure de donnée en Python si les valeurs booléennes et la valeur vide n’étaient pas écrites différemment : True, False et None.

>>> import ast

>>> ast.literal_eval('{"clé":"valeur","age":20, '
...                  '"tableau": [1, 2, 3, ["x", "y"]]}')
{'clé': 'valeur', 'age': 20, 'tableau': [1, 2, 3, ['x', 'y']]}

>>> ast.literal_eval('{"erreur": null}')
ValueError: malformed node or string: ...

Exemple

Le module json est des plus simples à utiliser. Il est présenté par le fameux Kenneith Reitz [Sch16] dans Hitchhiker’s Guide To Python.

L’API du module json est similaire à celle utilisée par marshal et pickle qui permettent de sérialiser des objets Python.

load():

charge un fichier JSON;

loads():

charge une chaîne de caractères;

dump():

écrit en JSON dans fichier;

dumps():

écrit en JSON dans une chaîne de caractères.

>>> import json

>>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
'["foo", {"bar": ["baz", null, 1.0, 2]}]'

>>> json.dumps("€")
'"\\u20ac"'

>>> json.loads('[1, 2, "Hello"]')
[1, 2, 'Hello']

Un exemple travaillant avec un fichier externe. Un point très important est que JSON est toujours encodé en UTF-8.

test.json
{
    "counter": 1
}
"""Exemple de lecture et d'écriture depuis et vers un fichier JSON."""

import json

entrée = "test.json"
sortie = "test.out.json"

with open(entrée, "r", encoding="utf-8") as fp:
    données = json.load(fp)

données["counter"] += 1

with open(sortie, "w", encoding="utf-8") as fp:
    json.dump(données, fp, sort_keys=True, indent=4)

Résultat :

test.out.json
{
    "counter": 2
}

Validation

jsonschema permet de valider un document JSON selon un modèle. Consultez la documentation de JSON Schema pour en savoir plus.

schema.json
{
    "type": "object",
    "properties": {
        "counter": {"type": "number"}
    }
}
"""Exemple de validation d'un fichier JSON avec JSON Schema."""

import json

from jsonschema import validate

filename = "test.json"
schema = "schema.json"

with open(schema, encoding="utf-8") as fp:
    sch = json.load(fp)

with open(filename, encoding="utf-8") as fp:
    data = json.load(fp)

validate(data, sch)
print(f"{filename} est valide selon {schema}.")

Formats binaires

Un document JSON est un fichier texte, qui consomme plus de place qu’une représentation binaire. Python offre un format de sérialisation en binaire nommé pickle, comme il est possible d’utiliser des bibliothèques externes telles que msgpack, ou Apache Thrift.

Pickle

pickle est le format utilisé par multiprocessing pour échanger des données entre différents processus Python. C’est un format qui est pratique mais non interopérable avec d’autres langages, voire même d’autres versions de Python.

>>> import json
>>> from urllib.request import urlopen
>>> document = json.load(urlopen('https://www.reddit.com/.json'))

>>> doc = json.dumps(document, separators=',:').encode("utf-8")
>>> len(doc)
77266

>>> import pickle
>>> pack = pickle.dumps(document, protocole=pickle.HIGHEST_PROTOCOL)
>>> len(pack)
52227

Ce format n’est pas recommandé pour de multiples raisons. La documentation du module informe que lire du pikle revient à faire un eval. Ben Frederickson [Fre14] mentionne notamment la lenteur et la taille du pickle.

MessagePack

En alternative à pickle, MessagePack permet de réduire efficacement l’espace nécessaire au stockage et à l’échange de tels documents. En Python, c’est le module msgpack.

>>> import json
>>> from urllib.request import urlopen
>>> document = json.load(urlopen('https://www.reddit.com/.json'))

>>> doc = json.dumps(document, separators=',:').encode("utf-8")
>>> len(doc)
77244

>>> import msgpack
>>> pack = msgpack.packb(document)
>>> len(pack)
67017

Notez que cette petite différence n’est plus forcément intéressante si le contenu est compressé à l’aide de gzip.

>>> import gzip

>>> len(gzip.compress(doc))
14532

>>> len(gzip.compress(pack))
15002

Parfois, le mieux est l’ennemi du bien.

Streaming

Autre inconvénient majeur vis-à-vis du format XML est qu’il n’est pas aisé de lire un document au fur et à mesure qu’il est reçu, en streaming. En XML, on utilise une API nommée SAX. json propose un modèle demandant de charger l’entier d’un document en mémoire. Comme avec DOM en XML. Ce problème se résout à l’aide de YAJL et du module ijson.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
"""Lecture en streaming d'un gros document JSON."""

from pprint import pprint
from urllib.request import urlopen

import ijson

f = urlopen('https://www.reddit.com/.json')

# data.children représente le chemin dans le fichier
# dont les éléments nous intéressent.
for child in ijson.items(f, 'data.children'):
    pprint(child)

Conclusion

JSON est un format de fichier à connaître, comprendre et savoir utiliser. Dans sa version basique, voire même dans sa version riche nommée JSON-LD utilisée par de nombreuses API. Si vous devez consommer des données JSON externe, il n’est que vivement recommandé d’ajouter un schéma afin d’offrir un message d’erreur adéquat en cas de non respect du document espéré. Et des solutions existent afin de contourner des problèmes de fichiers inutilement volumineux ou devant être chargés complètement en mémoire avoir de pouvoir être lus.

JSON c’est bon, mangez-en!

—Anonymous

1

<yoan.blanc@he-arc.ch>

Bra14

Tim Bray. The javascript object notation (json) data interchange format. ECMA International®, 2014. URL: http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf.

Fre14

Ben Frederickson. Don’t pickle your data. 2014. URL: http://www.benfrederickson.com/dont-pickle-your-data/.

Sch16

K.R.T. Schlusser. Hitchhiker’s Guide To Python. O’Reilly Media, Incorporated, 2016. ISBN 9781491933213. URL: http://docs.python-guide.org/en/latest/.