Javascript

JSON, le stockage léger et pratique de données multitypes

Article par (Traduction Open-source, développeur Web, France - Alsace)
Créé le , mis à jour le (67390 lectures)
Tags : javascript, ajax, json, json_decode

Voici une idée simple et efficace (comme je les aime) qui fait son chemin : le format .json. Ce format, provenant du monde JavaScript et représentant un objet, s'apparente au .XML, dans sa fonction du moins : il permet de stocker des données textuelles, mais pas des données binaires (du moins pas sans les encoder, en base64 par exemple) de manière structurée.

Il a été créé par Douglas Crockford qui l'a décrit en 2006 par la RFC4627 de l'IETF. Cette référence a été mise à jour en 2014 dans la RFC7159.

Avantages et inconvénients

Vous allez vous rendre compte que ce format ne présente pas beaucoup de défauts (mais peut-être ne suis-je pas tout à fait objectif en disant cela), principalement parce qu'il reste vraiment très simple à lire, comprendre et utiliser.

Les plus

  • Format compréhensible par tous (humain et machine). Aucun apprentissage n'est requis puisque la syntaxe n'utilise que quelques marques de ponctuations (nous le verrons plus tard).
  • Ne dépend d'aucun langage (format d'échange de données ouvert).Comme ce format est très ouvert, il est pris en charge par de nombreux langages : JavaScript, PHP, Perl, Python, Ruby, Java,...
  • Permet de stocker des données de différents types : chaînes de caractères (y compris des images en base64), nombres, tableaux (array), objets, booléens (true, false), la valeur null.
  • Sa structure en arborescence et sa syntaxe simple lui permet de rester très "léger" et efficace.

Les moins

  • Comme pour tout moyen de stockage de données, il faudra prendre des mesures de sécurité pour garantir la sécurité des données sensibles.
  • Le fait que la syntaxe soit rudimentaire peut être un inconvénient dans certains cas. Par exemple, contrairement à XML, il n'y a pas d'identification précise des données (sous forme de balise par exemple), la structure doit donc être connue avant utilisation.

La syntaxe

Je vous parlais précédemment de quelques signes de ponctuations qui permettent de structurer les données dans un fichier .json. En voici la liste (vous ne serez normalement pas dépaysés puisqu'ils sont utilisés exactement de la même manière dans d'autres langages, comme JavaScript par exemple) :

  • {...} : les accolades définissent un objet.
  • "language":"Java" : Les guillemets (double-quotes) et les double-points définissent un couple clé/valeur (on parle de membre).
  • [...] : Les crochets définissent un tableau (ou array en anglais).
  • {"id":1, "language":"json", "author":"Douglas Crockford"} : Les virgules permettent de séparer les membres d'un tableau ou, comme ici, d'un objet . A noter : pas de virgule pour le dernier membre d'un objet, sinon, il ne sera pas valide et vous aurez des erreurs lors de l'analyse du fichier.

C'est tout ? Ben oui, c'est tout ! Une dernière précision cependant, et elle n'est pas anodine : tout doit être encodé en utf-8 (ou utf-16 ou utf-32).

Il est temps de découvrir un exemple de fichier .json basique :

{
    "titre_album":"Abacab",
    "groupe":"Genesis",
    "annee":1981,
    "genre":"Rock"
}

Suffisamment simple pour ne pas avoir à donner d'explications, non ? Un fichier .json contient un objet ({...}) qui, ici, contient 4 membres identifiés par leurs paires clé/valeur. Les valeurs possibles sont :

  • Une chaîne de caractères : "titre":"Le format json", "description":"Le format <strong>simple</strong> et <strong>léger</strong>, "contenu":"<p>L'avantage de json est son incroyable simplicité d'apprentissage et de mise en oeuvre. C'est le \"Petit Poucet\" de l'échange de données.</p>"
  • Un nombre (pas de guillemets requis dans ce cas) : "pi":3.14, "g":9.81, "v_son":340
  • Un tableau : [...]
  • Un objet : {...}
  • D'autres valeurs possibles : un booléen (true ou false), null, rien ("alsanaute":true, "autrenaute":null,"bisounaute":""). Attention, ces valeurs doivent écrites en minuscule.

Sachez qu'il est possible d'ajouter autant d'espaces (au sens large du terme, c'est à dire que les retours à la ligne et les tabulations sont également considérés comme des espaces) que l'on veut. Le fichier ci-dessus pourra donc également s'écrire de la manière suivante, par exemple :

{
    "titre_album"
       :"Abacab",
    "groupe"
       :"Genesis",
    "annee"
       :1981,
    "genre"
       :"Rock"
 }

Voyons à présent un exemple plus complexe contenant des objets et des tableaux :

{
  "fruits": [
    { "kiwis": 3,
      "mangues": 4,
      "pommes": null
    },
    { "panier": true }
  ],
  "legumes": {
      "patates": "amandine",
      "poireaux": false
    },
    "viandes": ["poisson","poulet","boeuf"]
 }

Dans cet exemple nous pouvons observer qu'il y a 3 membres (fruits, legumes et viandes). fruits est constitué d'un seul membre qui est un tableau de 2 objets : le premier objet contient 3 membres et le second un seul. legumes est défini par un objet constitué par 2 membres. viandes, quant à lui, est défini par un tableau de 3 éléments.

Cet exemple démontre l'extraordinaire imbrication (illimitée) de ce type de format, encore une grande qualité à mettre à l'actif de .json !

Visualisation et validation

Quand on débute dans un langage, le fait d'avoir à disposition des outils permettant de visualiser et de valider le code est d'une grande aide. Là encore .json est doté de tout ce dont vous avez besoin. En effet, plusieurs visualiseurs/validateurs sont disponibles en ligne. Un des visualiseurs les plus connu est jsonviewer.stack.hu. Le grand avantage de cet outil est qu'il intègre toutes les fonctionnalités utiles pour l'analyse d'un fragment de code .json : un validateur (très basique puisqu'il ne fait qu'indiquer si le code contient des erreurs), un visualiseur, un minificateur. Le principe est simple, vous collez le code dans la fenêtre "Text" puis vous cliquez sur l'onglet "Viewer". S'il n'y a pas d'erreur dans votre code, l'arborescence de votre .json sera affichée. Copie d'écran pour le dernier exemple traité :

Visualiseur de fichier json

Pour la minification, Il suffit de cliquer sur le bouton "Remove white spaces". Pour revenir à la version formatée ou pour formater un fichier non formaté, cliquez sur le bouton "Format".

Le validateur se contente d'indiquer qu'il y a une erreur dans le code analysé, c'est un peu léger pour le débogage, qu'à cela ne tienne, il existe d'autres outils plus performants pour cela (ils indiquent l'endroit où se trouve l'erreur). Un exemple parmi d'autres : JSONLint.

D'autres outils disponibles en ligne :

PHP et json

Généralités

Le format .json est une notation héritée de JavaScript que l'on peut considérer comme un objet, c'est donc dans ce langage-là qu'ont été développés en premier des fonctions de prise en charge, notamment pour AJAX, pour lequel il permet de linéariser des données de requêtes. Voir aussi « Le format JSON, AJAX et jQuery ».

Mais, comme dit au début de ce tutoriel, beaucoup d'autres langages prennent en charge .json. Parmi eux, PHP.
Dans un premier temps, il était nécessaire d'installer une bibliothèque sur le serveur afin d'obtenir des outils de traitement, mais à présent (à partir de la version 5.2), ces outils sont intégrés dans le noyau de php, du moins les deux fonctions de décodage et d'encodage json entre autres : json_decode() et json_encode() . Nous utiliserons la première fonction dans le cas d'étude qui va suivre.

json pour la traduction de chaînes

Nous connaissons différents formats permettant l'internationalisation de fichiers, le plus connu d'entre eux étant gettext de la Free Software Foundation (FSF) (uniquement développé à des fins de traduction et utilisé notamment par différents CMS tel que WordPress), mais également d'autres langages pas uniquement dédié à cela comme XML, YAML. Ils ont tous fait leur preuve et sont donc fiables. Cependant, ils se trouvent être assez "lourd" et demandent donc des ressources certaines lors de leur analyse. C'est pourquoi, opter pour un (ou plusieurs) fichier .json semble être une alternative intéressante dans le cadre de "petits" projets à traduire. Il est léger, très rapide à analyser et garde les avantages de lisibilité qu'ont les moyens énumérés précédemment.

Nous allons donc voir comment mettre en place un système d'internationalisation de fichier en utilisant ce format. Voici le code du fichier que je vous propose de traiter :

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>Alsacreafans</title>
        <meta name="description" content="Communauté des fans d'Alsacreations.com">
    </head>
    
    <body>
        
        <header>
            <h1>Les alsacreafans</h1>
            <p>Nous pensons qu'Alsacreations est une initiative d'<a href="http://vosdroits.service-public.fr/associations/F1131.xhtml">utilité publique</a> !</p>
        </header>
        
        <main>
            <article>
                <header>
                    <h2>Vos avis</h2>
                </header>
                <p>Quelques commentaires des fans :</p>
                <blockquote>
                    <p>Des tutoriels très clairs, des articles toujours à la pointe de l'actualité !</p>
                </blockquote>
                <blockquote>
                    <p>Vous obtenez des avis de "pros" dans le forum d'Alsacreations.com !</p>
                </blockquote>
                <blockquote>
                    <p>Ma veille technologique passe par le flux RSS et la timeline Twitter d'Alsacreations.com !</p>
                </blockquote>
            </article>
        </main>
        
        <footer>
            <p>
                © Alsacreafans.com 2015 | Cet exemple de page html n'est pas de la responsabilité d'Alsacreations.com et n'a été mis en place que pour des raisons didactiques.
            </p>
        </footer>

    </body>
</html>

Nous allons donc créer un fichier fr.json qui stockera les chaînes en français, le voici (il faudra faire de même pour les autres langues, dans la démonstration ci-dessous, un fichier en.json) :

{
    "head_title":"Les Alsacreafans",
    "head_description":"Communauté des fans d'Alsacreations.com",
    "site_h1":"Les Alsacreafans",
    "site_description":"Nous pensons qu'Alsacreations est une initiative d'<a href=\"http://vosdroits.service-public.fr/associations/F1131.xhtml\">utilité publique</a> !",
    "page_h2":"Vos avis",
    "page_content":"<p>Quelques commentaires des fans :</p><blockquote><p>Des tutoriels très clairs, des articles toujours à la pointe de l'actualité !</p></blockquote><blockquote><p>Vous obtenez des avis de \"pros\" dans le forum d'Alsacreations !</p></blockquote><blockquote><p>Ma veille technologique passe par le flux RSS et la timeline Twitter d'Alsacreations !</p></blockquote>",
    "footer_text":"© Alsafans.com 2015 | Cet exemple de page html n'est pas de la responsabilité d'Alsacreations.com et n'a été mis en place que pour des raisons didactiques."
}

Quelques commentaires à propos de ce fichier :

  • Les clés sont écrites en anglais, langue de référence pour les traducteurs. Elles sont écrites en minuscule et les mots sont séparés par des "underscores" pour faciliter la sélection au double-clic. Il est tout à fait possible, d'après mes tests, d'utiliser des espaces dans les noms de clé. Par exemple, "The site header" est une valeur valide.

    Attention, les clés sont sensibles à la casse ("Site_H1""site_h1")

  • Dans notre exemple, la valeur de page_content ne doit pas comporter de retours à la ligne qui rendent le fichier invalide. Cela peut arriver lorsque l'on fait un copier/coller de la source.
  • Les commentaires ne sont pas pris en charge par le format .json. Mais une petite astuce pourra faire l'affaire : il suffit d'ajouter un membre assez visible et qui ne sera pas pris en compte pour la traduction. Par exemple : "Top_page_infos":"##########################"

Le fichier à internationaliser (qui est en fait un fichier .php) sera modifié de la manière suivante :

<?php
$lang = $fr_class = $en_class = '';
/* Récupération de la langue dans la chaîne get */
$lang = (isset($_GET['lang']) && file_exists('lang/'.$_GET['lang'].'.json')) ? $_GET['lang'] : 'fr';
/* Définition de la class pour les liens de langue */
if ($lang == 'fr')
    $fr_class = ' class="active"';
else
    $en_class = ' class="active"';
/* Récupération du contenu du fichier .json */
$contenu_fichier_json = file_get_contents('lang/'.$lang.'.json');
/* Les données sont récupérées sous forme de tableau (true) */
$tr = json_decode($contenu_fichier_json, true);
?>
<!DOCTYPE html>
<html lang="<?php echo $lang ?">
    <head>
        <meta charset="utf-8">
        <title><?php echo $tr['head_title'] ?></title>
        <meta name="description" content="<?php echo $tr['head_description'] ?>">
    </head>
    
    <body>
        
        <header>
            <h1><?php echo $tr['site_h1'] ?></h1>
            <p><?php echo $tr['site_description'] ?></p>
        </header>
        
        <nav><a<?php echo $en_class ?> href="?lang=en">en</a> <a<?php echo $fr_class ?> href="?lang=fr">fr</a></nav>
        
        <main>
            <article>
                <header>
                    <h2><?php echo $tr['page_h2'] ?></h2>
                </header>
                <?php echo $tr['page_content'] ?>
            </article>
        </main>
        
        <footer>
            <p>
                <?php echo $tr['footer_text'] ?>
            </p>
        </footer>

    </body>
</html>

Là encore quelques commentaires…

  • Le code php est pour le moins simplissime :
    1. Récupération du code iso de la langue dans la chaîne get ;
    2. définition de la class des liens de langue (pour le style des deux boutons selon l'état) ;
    3. récupération du contenu du fichier fr.json ou en.json;
    4. décodage du fichier ;
  • On retrouve toutes les valeurs sous forme de tableau associatif si on ajoute le paramètre true à la fonction json_decode(), sinon on les obtient sous forme d'objet. site_h1 par exemple, serait récupéré ainsi : $fr->{'site_h1'}.
  • On peut bien évidemment améliorer ce système d'internationalisation en créant des fonctions de traitement des traductions avant affichage à l'instar de la fonction _() de gettext.

Voir la démonstration

Ici, nous avons vu un cas très simple de décodage de fichier .json. La profondeur d'imbrication d'un tel fichier est tout aussi simple à prendre en compte, vous pouvez d'ailleurs vous aider des visualiseurs json pour retrouver les différents éléments de l'objet. Pour l'exemple json complexe, vous récupérerez la valeur de "mangues" par $fr['fruits'][0]['mangues'] (si vous utilisez le même code php que je vous propose ci-dessus).

Ressources

Commentaires

GizMecano a dit le

Voilà qui me semble un bon résumé des principales caractéristiques de ce format (désormais assez répandu). Peut-être pourrait-il être utile de dresser un petit comparatif avec d’autres alternatives, tel YAML (qui, bien que non directement utilisable en PHP, semble un peu mieux pris en charge par certains IDE, comme Aptana).

Nico3333fr a dit le

Merci beaucoup pour ces explications. :)

(je reconnais que j'apprécie de plus en plus ce format, bien pratique et simple)

lamext a dit le

J'ai noté quelques erreurs dans l'article :

"il permet de stocker des données textuelles (mais pas des données binaires)"

On peut stocker des données binaires en les encodant avec l'encodage de son choix base64, base85, code points...

"Langage compréhensible par tous"

JSON est un format, pas un langage.

"Permet de stocker des données de différents types : chaînes de caractères (y compris des images en base64), nombres, tableaux (array), objets."

Il manque les types booléen (true/false) et null dans la liste (on les retrouve pourtant dans les exemples, plus loin dans l'article). Il faut aussi noter que les nombres sont toujours écrits en décimal. Pour les grands nombres (big numbers) la majorité des parseurs permettent de les convertir à la volée en chaînes de caractères.

"Ne convient pas au stockage de données sensibles."

Ce point n'a pas de sens, on peut tout aussi bien stocker des données sensibles (par exemple des identifiants à une base de données) en json que dans d'autres formats comme YAML, XML, etc.

"La syntaxe ..."

Il manque la syntaxe pour les booleans (true, false, en minuscules, sans quotes), null (en minuscules, sans quotes) et nombres (toujours en décimal, sans quotes) dans les exemples.

"tout doit être encodé en utf-8."

C'est peut être le cas pour PHP mais le format JSON supporte aussi UTF-16 et UTF-32.

"Quand on débute dans un langage, ..."

Format, pas langage.

"Cet exemple démontre l'extraordinaire récursivité (illimitée ou presque : 512 niveaux acceptés !) de ce type de format, encore une grande qualité à mettre à l'actif de .json !"

Ce n'est pas de la récursivité mais de l'imbrication. JSON n'est pas limité a 512 niveaux d'imbrication, il n'a pas de limite théorique, on peut imbriquer des milliards d'objets/tableaux dans un document JSON. 512 est juste la valeur par défaut de la fonction json_decode en PHP, on peut la changer pour mettre ce qu'on veut.

"Nous connaissons différents langages ..."

Formats, pas langages.

"json pour la traduction de chaînes"

Dommage pour l'exemple en PHP de forcer la convertion en array alors que json_decode renvoi un objet standard dont la syntaxe est plus pratique que les array, ex : $tr->site_h1 vs $tr['site_h1']

"La récursivité d'un tel fichier est tout aussi simple à prendre en compte, ..."

Imbrication, pas récursivité.

niuxe a dit le

petit lien annexe : http://beta.json-generator.com/

jojaba a dit le

@lamext,

Merci pour ces réajustements pour lesquels je suis globalement d'accord. Dès que possible, je corrige ça.
Juste une petite justification concernant l'utilisation des array à la place des objets. Personnellement j'ai beaucoup plus l'habitude d'utiliser des tableaux pour analyser et trier des données et je pense que d'autres développeurs sont dans mon cas. Je parlais dans mon article d'amélioration du système de traduction, voici une fonction que j'utilise pour tariter les tarductinos avant de les envoyer :
======================
function __($key) {
global $tr;
if (array_key_exists($key, $tr) && $tr[$key] != '')
return $tr[$key];
else
return str_replace('_', ' ', $key);
}
======================
Le fait d'avoir utilisé un array a simplifié le traitement des cas lorsque la clé n'existe pas ou qu'elle n'a pas encore été traduite (la clé indiquée en paramètre est alors affichée).

Der Vogel a dit le

Pour info il existe JSON-LD, une recommandation du W3C basée sur JSON. C'est LE format recommandé pour échanger les données.
Voici quelques liens en anglais :
- le site officiel : json-ld.org
- la recommandation du W3C : www.w3.org/TR/json-ld

lamext a dit le

@jojaba tu peux utiliser un objet de la même façon que tu utilises ton tableau :

function __($key) {
global $tr;

if (property_exists($tr, $key) && $tr->$key != '') {
return $tr->$key;
}

return str_replace('_', ' ', $key);
}

Un isset pour remplacer property_exists ou array_key_exists fonctionnerait d'ailleurs tout aussi bien.

raphhh a dit le

Merci pour cet article qui résume bien la simplicité et l'interopérabilité de ce format de données.

"Ce format (...) représentant un objet (...)"
Attention qu'il s'agit d'un raccourci erroné: un json n'est pas forcément un objet. Il peut être n'importe quelle valeur valide (array, bool, string, number, null). On peut donc avoir un json comme ceci '[...]' par exemple.

Parmi les inconvénients du json, il faut également citer l'absence de commentaires, ce qui en fait très un mauvais format de config. Sans doute pour cette raison notamment, le format tend à être délaissé au profit du yaml, lequel est davantage lisible pour un humain. https://twitter.com/benjammingh/status/511959787865919489

Attention aussi que, en PHP, l'utilisation de json_decode peut, dans certains cas extrêmes, être ambiguë: http://www.thedarksideofthewebblog.com/les-pe...

Sinon, voici deux librairies sur le maniement du json en PHP:
Un mapper pour objet: https://github.com/netresearch/jsonmapper
Un CRUD pour fichier json: https://github.com/Raphhh/balloon

jojaba a dit le

@lamext
Tu auras compris que je ne suis pas autant à l'aise avec les objets qu'avec les array.
Merci pour l'alternative objet. :)

@raphhh
Tu as raison, un json peut être également un array, la spécification le mentionne ici :
===========================
A JSON text is a serialized value. Note that certain previous
specifications of JSON constrained a JSON text to be an object or an array. Implementations that generate only objects or arrays where a
JSON text is called for will be interoperable in the sense that all
implementations will accept these as conforming JSON texts.
===========================
On peut peut-être considérer un array comme un objet... ;)
Quant à l'utilisation ambigüe avec php, cela sort du cadre de cet article je pense, mais c'est toujours bon à savoir.
Merci pour les deux bibliothèques.

stradfred a dit le

Si vous souhaitez un bon petit cms sympa développé avec ce format, je vous recommande 99ko => http://99ko.hellojo.fr dont je participe au projet ;)