JSON : les bonnes pratiques indispensables Un parseur JSON

Voici une implémentation d?un parseur JSON en JavaScript :

 var json_parse = function () {
// Voici une fonction capable de parser un texte JSON, en produisant
// une structure de données JavaScript. C?est un parseur récursif
// descendant simple.
// Nous définissons la fonction à l?intérieur d?une autre
// fonction afin d?éviter de créer des variables globales.
var at, // L?index du caractère courant
ch, // Le caractère courant
escapee = {
???: ???,
?\\?: ?\\?,
?/?: ?/?,
b: ?b?,
f: ?\f?,
n: ?\n?,
r: ?\r?,
t: ?\t?
},
text,
error = function (m) {
// Appeler error lorsque quelque chose ne va pas.
throw {
name: ?SyntaxError?,
message: m,
at: at,
text: text
};
},
next = function (c) {
// Si un paramètre c est fourni, vérifier qu?il correspond
// au caractère actuel.
if (c && c !== ch) {
error(?Expected ?? + c + ?? instead of ?? + ch + ???);
}
// Récupérer le caractère suivant. Lorsqu?il n?y a plus de caractère,
// renvoyer la chaîne vide.
ch = text.charAt(at);
at += 1;
return ch;
},
number = function () {
// Parser une valeur de nombre.
var number,
string = ??;
if (ch === ?-?) {
string = ?-?;
next(?-?);
}
while (ch >= ?0? && ch <= ?9?) {
string += ch;
next();
}
if (ch === ?.?) {
string += ?.?;
while (next() && ch >= ?0? && ch <= ?9?) {
string += ch;
}
}
if (ch === ?e? || ch === ?E?) {
string += ch;
next();
if (ch === ?-? || ch === ?+?) {
string += ch;
next();
}
while (ch >= ?0? && ch <= ?9?) {
string += ch;
next();
}
}
number = +string;
if (isNaN(number)) {
error(?Bad number?);
} else {
return number;
}
},
string = function () {
// Parser une valeur de chaîne.
var hex,
i,
string = ??,
uffff;
// Lors de l?analyse des valeurs de chaîne, nous devons rechercher
// les caractères ? et \.
if (ch === ?"?) {
while (next()) {
if (ch === ???) {
next();
return string;
} else if (ch === ?\\?) {
next();
if (ch === ?u?) {
uffff = 0;
for (i = 0; i < 4; i += 1) {
hex = parseInt(next(), 16);
if (!isFinite(hex)) {
break;
}
uffff = uffff * 16 + hex;
}
string += String.fromCharCode(uffff);
} else if (typeof escapee[ch] === ?string?) {
string += escapee[ch];
} else {
break;
}
} else {
string += ch;
}
}
}
error(?Bad string?);
},
white = function () {
// Ignorer l?espace blanc.
while (ch && ch <= ? ?) {
next();
}
},
word = function () {
// true, false ou null.
switch (ch) {
case ?t?:
next(?t?);
next(?r?);
next(?u?);
next(?e?);
return true;
case ?f?:
next(?f?);
next(?a?);
next(?l?);
next(?s?);
next(?e?);
return false;
case ?n?:
next(?n?);
next(?u?);
next(?l?);
next(?l?);
return null;
}
error(?Unexpected ?? + ch + ???);
},
value, // Espace réservé pour la fonction value.
array = function () {
// Parser une valeur de tableau.
var array = [];
if (ch === ?[?) {
next(?[?);
white();
if (ch === ?]?) {
next(?]?);
return array; // tableau vide
}
while (ch) {
array.push(value());
white();
if (ch === ?]?) {
next(?]?);
return array;
}
next(?,?);
white();
}
}
error(?Bad array?);
},
object = function () {
// Parser une valeur d?objet.
var key,
object = {};
if (ch === ?{?) {
next(?{?);
white();
if (ch === ?}?) {
next(?}?);
return object; // objet vide
}
while (ch) {
key = string();
white();
next(?:?);
object[key] = value();
white();
if (ch === ?}?) {
next(?}?);
return object;
}
next(?,?);
white();
}
}
error(?Bad object?);
};
value = function () {
// Parser une valeur JSON. Il peut s?agir d?un objet, d?un tableau
// d?une chaîne, d?un nombre ou d?un mot.
white();
switch (ch) {
case ?{?:
return object();
case ?[?:
return array();
case ???:
return string();
case ?-?:
return number();
default:
return ch >= ?0? && ch <= ?9? ? number() : word();
}
};
// Renvoyer la fonction json_parse. Elle aura accès à toutes les fonctions
// et variables précédentes.
return function (source, reviver) {
var result;
text = source;
at = 0;
ch = ? ?;
result = value();
white();
if (ch) {
error(?Syntax error?);
}
// S?il y a une fonction reviver, nous parcourons la nouvelle structure
// de manière récursive, en passant chaque paire nom/valeur à la
// fonction reviver pour une éventuelle transformation, en commençant
// par un objet d?amorçage temporaire qui contient le résultat
// dans une clé vide. S?il n?y a pas de fonction reviver, nous
// renvoyons simplement le résultat.
return typeof reviver === ?function? ?
function walk(holder, key) {
var k, v, value = holder[key];
if (value && typeof value === ?object?) {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}({??: result}, ??) : result;
};
}();