JSON - JavaScript Object Notation : Présentation

13 janvier 2007JSON, Javascript, validation
 Cet article a été rédigé il y a plusieurs années et peut ne plus être tout à fait à jour…

JSON est l'abréviation de "JavaScript Object Notation".

En quelques mots, JSON est une manière d'écrire des objets basée sur la syntaxe Javascript.

Le format JSON est défini par la RFC 4627, postée en Juillet 2006 par Douglas Crockford :

(extrait)
JavaScript Object Notation (JSON) is a text format for the serialization of structured data. It is derived from the object literals of JavaScript, as defined in the ECMAScript Programming Language Standard, Third Edition [ECMA].

(La traduction suivante est de moi)

La Notation Objet JavaScript est un format texte pour la sérialisation de données structurées. Il est dérivé des objets litéraux de JavaScript, tels qu'ils sont définis dans le Standard du Langage de Programmation ECMAScript, 3ème édition

Cela signifie que JSON est un format que l'on peut utiliser pour représenter des objets sous forme d'une chaîne de caractères, en utilisation une notation compatible avec JavaScript...

... le principal avantage étant que cette notation JavaScript permettra une manipulation aisée de ces objets sérialisés[1].

Données utilisant la Notation JavaScript Objet

Pour commencer, quelques mots sur la notation litéral objet de JavaScript, dont JSON est un sous-ensemble.

Déclarer un Objet

En JavaScript, un objet (simple, dans cet exemple) se déclare comme ceci :

var personne = {
        nom: 'Lerdorf',
        prenom: 'Rasmus'
    };

Et l'accès à ses propriétés se fait à l'aide de la syntaxe objet.propriete.
Par exemple, pour afficher le prénom et le nom de notre personne[2], nous pourrions écrire :

alert(personne.prenom + ' ' + personne.nom);

Ce qui afficherait effectivement :

Rasmus Lerdorf

Déclarer un tableau

En suivant la syntaxe de JavaScript, nous pouvons créer un tableau d'objets[3] :

var personnes = [
        {
            nom: 'Lerdorf',
            prenom: 'Rasmus'
        },
        {
            nom: 'Gutmans',
            prenom: 'Andi'
        },
        {
            nom: 'Suraski',
            prenom: 'Zeev'
        }
    ];

Et l'accès à ses composantes se fait comme pour tout tableau/objet JavaScript.
Par exemple :

alert(personnes[1].prenom + ' ' + personnes[1].nom);

Affichera :

Andi Gutmans

JSON et données sérialisées

Savoir comment déclarer un objet en JavaScript, c'est bien... Mais ce n'est pas le but de cet article.
En effet, un objet JavaScript est propre au JavaScript : il ne peut pas être généré, ni n'est compréhensible, par d'autres langages.

Par contre, un chaîne de caractères, elle, est une notion commune à la plupart des langages de programmation.
Que donnerait un de nos objets une fois écrit sous forme d'une chaîne de caractères ?

Objet sérializé

En JavaScript, voici ce qu'on obtiendrait :

var strPersonne = "{nom: 'Lerdorf', prenom: 'Rasmus'}";

Nous avons juste entouré notre déclaration d'objet par des guillemets, et supprimé les espace d'indentation.
Et nous avons maintenant une chaîne de caractères JS valide :

alert(strPersonne);

Affichera :

{nom: 'Lerdorf', prenom: 'Rasmus'}

Ce qui n'est pas utilisable tel quel...

eval

Mais JavaScript propose une fonction nommée eval, qui permet d'exécuter du code stocké dans une chaîne de caractères.
Et JSON est un sous-ensemble de la notation litérale objet de JavaScript... ce qui signifie qu'il s'agit de code JS valide - et donc, evaluable.

Il faut juste rajouter des parenthèses autour de la déclaration d'objet, comme ceci :

var personne = eval('(' + strPersonne + ')');

Et personne sera exactement le même objet que le tout premier que nous avons présenté.
Ainsi,

alert(personne.prenom + ' ' + personne.nom);

affichera bien

Rasmus Lerdorf

Types de données JSON

JSON permet d'utiliser plusieurs types de données :

  • Numérique : nombres entiers ou à virgule : 18, 3.1415
  • Chaîne de caractères : suite de caractères Unicode, encadrés par des guillemets doubles. Le caractère d'échappement est l'antislash[4] : "Hello World", "Retour\nà la ligne\tet tabulation"
  • Booléen : true ou false
  • Tableau (Array) : Une liste ordonnées de valeurs, séparées par des virgules, et entourée de crochets : [3.1415, 10, "coucou", true]
  • Objet : Collection de couples clef/valeur, séparés par des virgules, et entourée d'accolades : {"pi": 3.1415, "mot": "coucou", "actif": true}. Les clefs sont des chaînes de caractères, donc, entre guillemets doubles[5].
  • null

Chaque valeur d'un tableau ou d'un objet peut être d'un de ces types.
Encore une fois, attention : les noms de propriétés d'objets sont des chaînes de caractères, donc, doivent être entourés de guillemets doubles ! Même si ce n'est pas nécessaire en JavaScript, ça l'est pour être conforme au format JSON !

Pour quelques illustrations, vous pouvez consulter la documentation en français de json.org.

JSON : Uniquement des données

Notation Litérale Objet JavaScript et méthodes

En utilisant la notation litérale d'objets de JavaScript, nous pouvons créer un objet représentant une personne habitant à Lyon et se nommant John Smith :

var personne = {
        nom: 'Smith',
        prenom: 'John',
        ville: 'Avignon',
        saluer: function (nom) {
                    alert(this.prenom + ' '
                          + this.nom
                          + ' salue ' + nom);
                },
        demenager: function (nouvelleVille) {
                    this.ville = nouvelleVille;
                    alert(this.prenom + ' '
                          + this.nom
                          + ' habite maintenant '
                          + this.ville);
                }
    };

Cette personne peut en saluer d'autre, et déménager.
Par exemple, pour saluer Bob, puis déménager à Lyon :

personne.saluer('Bob');
personne.demenager('Lyon');

Ce qui affichera successivement :

John Smith salue Bob

et :

John Smith habite maintenant Lyon

Et, bien entendu, la propriété ville de notre personne vaudra 'Lyon'.

JSON : un format d'échange de données

Cependant, JSON n'est qu'un sous-ensemble de la notation litérale d'objets de JavaScript, créé pour servir de format d'échange de données.

En conséquence, une chaîne de caractères au format JSON ne doit pas contenir de déclaration de méthode, mais uniquement des données.

La chaîne suivante (découpée uniquement pour des raisons de présentation, pour limiter le défilement horizontal) :

var strPersonne = '{"nom": "Smith", "prenom": "John", "ville": "Avignon", '
    + '"saluer": function (nom) {alert(this.prenom + \' \' + this.nom + '
    + '\' salue \' + nom);}, "demenager": function (nouvelleVille) '
    + '{this.ville = nouvelleVille; alert(this.prenom + \' \' + '
    + 'this.nom  + \' habite maintenant \' + this.ville);}}';

(oui, les guillemets doubles tout le long ne simplifient pas la lecture... Mais ils sont requis par la spécification JSON)

... passe parfaitement si on l'appelle avec eval :

var personne = eval('(' + strPersonne + ')');
personne.saluer('Bob');
personne.demenager('Lyon');

... et elle causera les deux mêmes affichages que précédemment.

Mais ce n'est pas une chaîne de caractères JSON valide, puisqu'elle contient des définitions de méthodes - ceci, pour raisons de sécurité : si vous vous assurez qu'une chaîne est du JSON valide avant de l'evaluer, vous saurez que l'objet obtenu ne contient que des données, et pas de méthode.

Autrement dit, en validant une chaîne de caractères JSON avant de l'évaluer, vous protégez votre application contre d'éventuelles injections de méthodes.

Validation de données JSON

Pour valider qu'une chaîne est du JSON valide avant de l'évaluer, utilisez le code suivant (donné en exemple au sein de la RFC 4627 évoquée plus haut) :

var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
             text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
         eval('(' + text + ')');

Le code suivant :

var strPersonne = '{"nom": "Smith", "prenom": "John", "ville": "Avignon"}';

var personne = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
                    strPersonne.replace(/"(\\.|[^"\\])*"/g, '')))
                && eval('(' + strPersonne + ')');

if (personne !== false)
{
    alert(personne.prenom + ' ' + personne.nom);
}
else
{
    alert('JSON invalide');
}

affichera

John Smith

En effet, la chaîne de caractères utilisée est du JSON valide :

  • uniquement des propriétés
  • les chaînes de caractères, y compris les noms de propriétés, sont entre guillemets doubles


Par contre, la portion de code suivante :

var strPersonne = '{"nom": "Smith", "prenom": "John", "ville": "Avignon", '
    + '"saluer": function (nom) {alert(this.prenom + \' \' + this.nom + '
    + '\' salue \' + nom);}, "demenager": function (nouvelleVille) '
    + '{this.ville = nouvelleVille; alert(this.prenom + \' \' + '
    + 'this.nom  + \' habite maintenant \' + this.ville);}}';

var personne = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
                    strPersonne.replace(/"(\\.|[^"\\])*"/g, '')))
                && eval('(' + text + ')');

if (personne !== false)
{
    personne.saluer('Bob');
}
else
{
    alert('JSON invalide');
}

affichera

JSON invalide

Puisque la chaîne de caractère que nous avons essayé de valider contient des définitions de méthodes, ce qui n'est pas permis par la spécification JSON.


Pour résumer, les données JSON sont stockées sous forme d'un texte, et de manière simple à encoder/décoder.

Cela explique pourquoi JSON peut être employé pour transmettre des données entre différents programmes, tels un programme serveur, et du JavaScript au sein d'un navigateur Web.

En particulier, il n'est pas rare d'utiliser JSON plutôt que XML pour la transmission de données lors de requêtes Ajax... Nous en reparlerons !

Notes

[1] La sérialisation étant le processus visant à encoder l'état d'un objet qui est en mémoire sous la forme d'une chaîne d'octets dans un flux de données - Wikipedia : Sérialisation

[2] Rasmus Lerdorf est le créateur du langage PHP

[3] Andi Gutmans et Zeev Suraski sont les créateurs de PHP3, en 1997 ; ils ont écrit la nouvelle version du Parser du futur PHP, le nommant Zend Engine ; ils sont les créateurs de Zend Technologies

[4] Notez que, selon le langage de programmation à partir duquel vous générez la chaîne JSON, il faudra que l'antislash soit lui-même échappé !

[5] J'insiste, mais je sais à quel point ils sont faciles à oublier, puisqu'ils ne sont pas obligatoires en JavaScript...