Formats, encodage, XML

Le format JSON, AJAX et jQuery

Article par (Alsacréations, Strasbourg)
Créé le , mis à jour le (262496 lectures)
Tags : javascript, jquery, xml, json, jsonp

JSON

JSON (JavaScript Object Notation) est une forme d'écriture de données en JavaScript.

Son avantage est de fournir un support pour une écriture simple et légère au format texte, relativement compréhensible par les développeurs JavaScript, mais aussi - et surtout - d'être nativement interprété contrairement au XML qui fait appel à de l'analyse syntaxique et parfois à DOM/XSLT pour accéder à sa structure et à son contenu. Il s'agit donc d'une arborescence de données, inspirée de XML mais dont l'emploi en JavaScript est plus aisé et plus performant, à partir du moment où on en connaît la structure.

On retrouve des facilités d'utilisation de JSON dans des frameworks tels que jQuery avec des fonctions d'aide à la création d'appels AJAX (surtout $.getJSON) pour lequel JSON est bien adapté.

Néanmoins, il existe d'autres implémentations dans une multitude d'autres langages pour se servir de JSON, il n'est pas limité à JavaScript : consultez une liste sur json.org. De même, il est possible d'en générer très facilement du côté du serveur - éventuellement après interrogation d'une base de données - car il s'agit bien d'un format texte pur ce qui rentre dans le champ de compétences de PHP, Java, ASP/.Net et consorts. On évitera son emploi pour manipuler du binaire.

Json Stthm

Principe

Les types utilisables sont issus de JavaScript, on retrouve notamment tout ce qui est booléen (Boolean), valeur numérique (Number), chaîne de texte (String), tableau (Array), objet (Object) ou null. On combine le tout au sein d'un objet, le plus souvent, pour utiliser une notation clé:valeur afin de retrouver les variables dans l'arborescence des données.

"clef" : "valeur"

Note : les exemples suivants ne mentionnent pas la balise <script> mais celle-ci est bien sûr obligatoire pour placer du JavaScript au sein d'un document HTML.

Exemple complet

Avec JavaScript, une déclaration peut être effectuée de la sorte pour stocker un objet dans une variable :

var courses = {
  "fruits": [
    { "kiwis": 3,
      "mangues": 4,
      "pommes": null
    },
    { "panier": true },
  ],
  "legumes":
    { "patates": "amandine",
      "figues": "de barbarie",
      "poireaux": false
    }
};

Voici un équivalent XML à titre d'information :

<?xml version="1.0" ?>
<root>
  <fruits>
    <item>
      <kiwis>3</kiwis>
      <mangues>4</mangues>
      <pommes></pommes>
    </item>
    <item>
      <panier>true</panier>
    </item>
  </fruits>
  <legumes>
    <patates>amandine</patates>
    <figues>de barbarie</figues>
    <poireaux>false</poireaux>
  </legumes>
</root>

On peut visualiser le contenu de la variable sous forme d'arborescence grâce à la console JavaScript et la fonction console.log (sous Firefox équipé de Firebug par exemple, en ayant activé l'onglet Console ou bien dans les Outils de développement des autres navigateurs) :

console.log(courses);

JSON et Console

Pour lire une valeur en particulier, on accède aux propriétés avec le point comme signe de séparation :

courses.fruits[0].kiwis;
// Retourne 3

Eval, chaînes JSON et parsing natif

La puissance de JSON provient du fait que l'on peut récupérer une chaîne de texte ou un fichier dans ce format pour l'exploiter directement avec la fonction eval() de JavaScript afin de constituer un objet. On peut alors s'adresser directement aux propriétés membres de cet objet (et de ses sous-membres) sans avoir à écrire d'autres fonctions d'analyse du texte. Dans l'exemple suivant, on suppose que la chaîne de caractères textjson contient du code JSON récupéré via un fichier intermédiaire ou via le retour d'un appel AJAX grâce à XMLHttpRequest.

var textejson = '{"kiwis": 3, "url": "http://www.alsacreations.com"}';
var objet = eval('(' + textejson + ')');
console.log(objet);

Dans l'absolu, on considère qu'il est plus sain de mettre en œuvre un parseur JSON spécifique plutôt qu'eval, si l'on ne fait pas absolument confiance à la source délivrant du JSON, étant donné que tout code JavaScript pourrait être interprété, éventuellement malicieusement. La plupart des navigateurs récents intègrent ce type de fonctionnalité avec une interface d'encodage/décodage :

  • Mozilla Firefox 3.5+
  • Microsoft Internet Explorer 8+
  • Google Chrome et Safari grâce à Webkit
  • Opera 10.5+
  • Les navigateurs mobiles

On trouve aussi des librairies JavaScript telles que Json-sans-eval qui sont moins performantes. Les frameworks jQuery, Dojo, Prototype, Mootols, Yahoo!UI font automatiquement appel à la librairie native si celle-ci est présente.

var textejson = '{"kiwis": 3, "url": "http://www.alsacreations.com"}';
var courses = JSON.parse(textejson);
console.log(courses);

De la même façon, on peut effectuer l'opération inverse et créer une chaîne de texte JSON à partir d'une structure de données JavaScript (tableau, liste de valeurs, etc).

var textejson = JSON.stringify({"kiwis":3,"mangues":4})
console.log(textejson);

Pour détecter la présence de cette interface JSON :

if(typeof JSON!="undefined") // OK

Utilisation pratique en AJAX avec XMLHttpRequest et jQuery

XMLHttpRequest est l'interface de base pour effectuer un appel AJAX avec JavaScript. Il convient bien entendu d'interroger la bonne url avec la méthode GET ou POST appropriée et de développer côté serveur le script fournissant une réponse au format JSON.

var courses = {};
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", url, true);
xmlhttp.onreadystatechange = function () {
  if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
    courses = JSON.parse(xmlhttp.responseText);
  }
};
xmlhttp.send(null);

Attention : cet exemple n'est pas compatible multi-navigateurs, ceux-ci implémentant l'objet XMLHttpRequest de différentes manières, notamment les anciennes versions d'Internet Explorer au travers des ActiveX.

L'intérêt de JSON avec les fonctions proposées par jQuery est de pouvoir effectuer des appels AJAX inter-domaines de manière simplifiée. En effet, les restrictions sur les autres types de données (html, texte, fichiers binaires) sont fortes : pour des raisons de sécurité de navigation et de protection des informations sur un domaine (en évitant le Cross-Site Scripting et le vol de contenu d'un site à l'autre) on se limite généralement à autoriser l'usage de JSON par le navigateur client en JavaScript. L'adresse appelée par l'objet XMLHttpRequest doit résider dans le même domaine que le serveur hébergeant la page d'origine effectuant l'appel. Bien entendu les autres langages et fichiers restent disponibles du point de vue développement serveur si celui-ci autorise les connexions sortantes, par exemple pour récupérer grâce à PHP un fichier XML distant sur un serveur HTTP.

Grâce à une déclinaison nommée JSONP (JSON with padding), une requête inter-domaine est possible par l'utilisation d'une fonction JavaScript de callback (que l'on nomme donc padding mais qui n'a rien à voir avec l'équivalent en CSS). Celle-ci est renvoyée par le serveur au client, qui l'exécute au retour, autorisant ce même client à charger des données JSON depuis un domaine externe, et donc à notifier l'objet appelant avec cette fonction de callback.

JSON/JSONP est proposé de la sorte par de nombreux services en ligne, telles que l'API Flickr.

La première étape est d'encapsuler les données - ici des statistiques - dans une fonction de callback, dont on choisit le nom, par exemple jsoncallback().

jsoncallback({demandes: 6490, offres: 16804, membres:32908, posts:364558, docs:864});

Cette chaîne de texte sera retournée par une URL en HTTP, par exemple http://www.mondomaine.com/statistiques.php?jsoncallback=?. Par convention, on se sert du dernier argument de l'URL pour placer le nom de la fonction de retour. jQuery saura remplacer le point d'interrogation final par cette fonction de callback à exécuter côté client. On utilise pour cela $.getJSON() dès que la page est chargée, qui est une variante des fonctions AJAX de base proposées par jQuery.

Il suffit sur la page destinée à recevoir les informations d'implémenter le tout et de traiter les données reçues. Dans le cas présent, on "remplit" des éléments span en se basant sur leurs identifiants, grâce à la fonction text() de jQuery. L'objet data passé en argument à la fonction de callback jsoncallback reçoit les données si l'appel se déroule avec succès.

<p>Nous avons <span id="annonces"></span> annonces d'emploi et <span id="docs"></span> documents en ligne.</p>
<p>Sur le forum, <span id="membres"></span> membres ont écrit <span id="posts"></span> messages.</p>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<script>
function jsoncallback(data){
  $("#annonces").text(data.offres+data.demandes);
  $("#posts").text(data.posts);
  $("#membres").text(data.membres);
  $("#docs").text(data.docs);
}
$(document).ready(function() {
  $.getJSON("http://www.mondomaine.com/statistiques.php?jsoncallback=?");
});
</script>

Sans avoir besoin d'utiliser absolument jQuery, on peut aussi exploiter de façon "naïve" la balise <script> qui est autorisée à effectuer des requêtes sur d'autres domaines. Dans l'exemple ci-dessous on affiche une des statistiques avec la fonction alert() de JavaScript.

<script>
function jsoncallback(data) {
  alert("Il y a "+data.membres+" membres");
}
</script>
<script src="http://www.mondomaine.com/statistiques.php"></script>

Note : l'attribut type="text/javascript" sur la balise script est ici omis dans le cas de HTML5 mais recommandé pour les versions précédentes.

Soyez bien prudents et ne divulguez pas d'informations sensibles par ce biais. Comme indiqué, les navigateurs ont toujours toléré les appels inter-domaines sans respecter d'obligation de provenance pour la balise <script>, ce qui peut être la source d'attaques de type CSRF (ou XSRF). Les données sont alors divulguées à une page tiers qui peut les exploiter de n'importe quelle façon, notamment si un utilisateur est identifié sur un site et que des informations de session (identifiants, mot de passe) y sont véhiculées en clair.

Ressources

Commentaires

thoferon a dit le

A noter qu'il faut bien réfléchir avant de choisir le format utilisé, d'autant plus qu'avec jQuery par exemple l'utilisation des différents formats possibles n'est pas vraiment plus difficile les uns par rapport aux autres

Pour comprendre pourquoi JSON peut ne pas être le meilleur choix, voici l'article "Pourquoi faut-il abandonner JSON" que j'avais écrit il y a peu de temps
http://www.backsmash.net/148-pourquoi-faut-il...

Skoua a dit le

En terme de performances, à partir de quelle quantité de contenu on peut se dire qu'il vaut mieux utiliser json plutôt que du xml ?

Si par exemple j'ai un fichier xml qui fait environ 100 lignes et que je veux le parser avec jQuery pour l'utiliser dans ma page, est-ce que je peux rester avec du xml ou serait-ce plus intéressant d'utiliser du json ?
Par intéressant je veux dire est-ce que je vais gagner plus que 3ms dont je me moque un peu ? :p

Merci.

dew a dit le

@thoferon : Très bel article, effectivement il y a toujours des avantages et inconvénients à utiliser l'une ou l'autre technologie. Le but est bien entendu de ne pas utiliser JSON à toutes les sauces. En ce qui concerne la fuite d'informations, c'est bien pour cela qu'il est précisé de faire attention à la nature des données véhiculées. La meilleure façon d'éviter ce problème c'est de ne pas prendre ce risque. Quant aux erreurs silencieuses et aux inconvénients d'eval, ils sont à peu près évités par l'utilisation d'un framework (gestionnaire d'erreurs) et de l'interface native JSON (assez répandue maintenant).

thoferon a dit le

@Skoua : Un fichier de 100 lignes, ca peut faire une différence non-négligeable en terme de taille par rapport à JSON, et donc ralentir le chargement pour les petites connexions (les autres ne verront pas la différence). Pour le traitement, ça ne devrait pas causer de différences trop importantes, donc c'est négligeable.

@dew : "Très bel article" > Merci ;)
Concernant les frameworks et JSON.parse, c'est vrai que ça commence à résoudre ce problème-là, c'est déjà ça même si ce n'est pas tout