.. _json-tutorial:
``json``
========
.. image:: ../_static/json.png
:align: right
:alt: JSON logo
Par Yoan Blanc [#yb]_
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 :cite:`bray2014javascript`, 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``.
.. code-block:: json
{
"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``.
.. literalinclude:: ./examples/ast.pycon
:language: pycon
Exemple
-------
Le module :py:mod:`json` est des plus simples à utiliser. Il est présenté par
le fameux Kenneith Reitz :cite:`schlusser2016` dans `Hitchhiker's Guide To Python
`_.
L'API du module :py:mod:`json` est similaire à celle utilisée par
:py:mod:`marshal` et :py:mod:`pickle` qui permettent de sérialiser des objets
Python.
:py:func:`~json.load`:
charge un fichier JSON;
:py:func:`~json.loads`:
charge une chaîne de caractères;
:py:func:`~json.dump`:
écrit en JSON dans fichier;
:py:func:`~json.dumps`:
écrit en JSON dans une chaîne de caractères.
.. code-block:: pycon
>>> 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.
.. literalinclude:: ./examples/test.json
:caption: test.json
.. literalinclude:: ./examples/example.py
Résultat :
.. literalinclude:: ./examples/test.out.json
:caption: test.out.json
Validation
----------
:py:mod:`jsonschema` permet de valider un document JSON selon un modèle.
Consultez la documentation de `JSON Schema`_ pour en savoir plus.
.. literalinclude:: ./examples/schema.json
:caption: schema.json
.. literalinclude:: ./examples/validation.py
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é :py:mod:`pickle`, comme il est possible d'utiliser des bibliothèques
externes telles que :py:mod:`msgpack`, ou `Apache Thrift`_.
Pickle
^^^^^^
:py:mod:`pickle` est le format utilisé par :ref:`multiprocessing-tutorial`
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.
.. literalinclude:: ./examples/pickle.pycon
:language: pycon
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 :cite:`fred2014` 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 :py:mod:`msgpack`.
.. literalinclude:: ./examples/msg.pycon
:language: pycon
Notez que cette petite différence n'est plus forcément intéressante si le
contenu est compressé à l'aide de :py:mod:`gzip`.
.. literalinclude:: ./examples/gzip.pycon
:language: pycon
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 :abbr:`SAX (Simple API for XML)`. :py:mod:`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`_.
.. literalinclude:: ./examples/stream.py
:linenos:
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
.. [#yb]
.. _JSON: http://json.org/
.. _JSON Schema: http://json-schema.org/
.. _JSON-LD: http://json-ld.org/
.. _MessagePack: http://msgpack.org/
.. _YAJL: http://lloyd.github.io/yajl/
.. _ijson: https://pypi.python.org/pypi/ijson/
.. _Apache Thrift: http://thrift.apache.org/
.. _XML: https://www.w3.org/XML/
.. bibliography:: refs.bib