08 septembre 2011

Underscore + JSONSelect = XPath for JSON ?

XML est largement utilisé pour formater, stocker, échanger des informations structurées. Et malgré l’étendu de ses capacités, il n'en reste pas moins qu'un support. Et en tant que support JSON n'a rien à envier à XML. N'importe quelle information peut-être stocker avec JSON. Par contre l'écosystème XML est riche et on peut facilement transformer, valider, émietter nos données, ce qui semble moins évident avec JSON. Mais qui dit JSON dit Javascript et l'écosystème Javascript est également très riche et il permet de faire aussi bien voir mieux que XML dans certain domaine. Voici une technique pour picorer de l'information comme on le ferai avec XPath sur du XML mais sur un objet JSON.

JSONSelect

JSONSelect permet de picorer dans un objet javascript à l'aide de sélecteur CSS. Exemple :
#!/usr/bin/env node

var jsel = require('JSONSelect');
var doc = {
	"id": "0001",
	"type": "donut",
	"name": "Cake",
	"ppu": 0.55,
	"batters":
		{
			"batter":
				[
					{ "id": "1000", "type": "None" },
					{ "id": "1001", "type": "Regular" },
					{ "id": "1002", "type": "Chocolate" },
					{ "id": "1003", "type": "Blueberry" },
					{ "id": "1004", "type": "Devil's Food" }
				]
		},
	"topping":
		[
			{ "id": "5001", "type": "None" },
			{ "id": "5002", "type": "Glazed" },
			{ "id": "5005", "type": "Sugar" },
			{ "id": "5007", "type": "Powdered Sugar" },
			{ "id": "5006", "type": "Chocolate with Sprinkles" },
			{ "id": "5003", "type": "Chocolate" },
			{ "id": "5004", "type": "Maple" }
		]
};

console.log(jsel.match('.name', doc));
// [ 'Cake' ]
console.log(jsel.match('number', doc));
// [ 0.55 ]
console.log(jsel.match('.batter .type', doc));
// [ 'Regular', 'Chocolate', 'Blueberry', 'Devil\'s Food' ]
console.log(jsel.match('.type ~ .id:val("5005")', doc));
// [ '5005' ]
console.log(jsel.match('.type:val("None") ~ .id', doc));
// [ '1000', '5001' ]



Underscore

Underscore fournit ~ 60 fonctions orientées programmation fonctionnelle.
Et partie elles, une trentaine simplifient grandement la manipulation et le traitement des listes et des tableaux. Exemple :
#!/usr/bin/env node

var _ = require('underscore');

var arr = ["1000", "1001", "1002", "1003", "1004"];
console.log(_.first(arr));
// [ '1000' ]
console.log(_.without(arr, "1000"));
// [ '1001', '1002', '1003', '1004' ]
console.log(_(arr).chain().without(arr, "1000").first().value());
// 1001
console.log(_.map(arr, function(value) {
			return '#'+value ;
}));
// [ '#1000', '#1001', '#1002', '#1003', '#1004' ]
console.log(_(arr).chain().map(function(value) { 
			return value.substr(1, 2);
	}).uniq().first().value());
// 00


JSONSelect & Underscore

JSONSelect permet de récupérer des listes d'objet ou des tableaux de valeur, Underscore permet de manipuler des listes et des tableaux.
Coupler les deux offre donc un outil extrêmement puissant pour aller piocher et trier de l'information. Exemple :
#!/usr/bin/env node

var jsel = require('JSONSelect'),
	_ = require('underscore');

//  Underscore + JSONSelect
_.mixin({
		jsel : function(doc, sel) {
			return jsel.match(sel, doc)
		}
});

var data = [
	{ name:'Africa', type:'continent'},
	{ name:'Egypt', type:'country' },
	{ name:'Kenya', type:'country'},
	{ name:'Nairobi', type:'city' },
	{ name:'Mombasa', type:'city' },
	{ name:'Sudan', type:'country'},
	{ name:'Khartoum', type:'city' },
	{ name:'Asia', type:'continent'},
	{ name:'China', type:'country' },
	{ name:'India', type:'country' },
	{ name:'Russia', type:'country' },
	{ name:'Mongolia', type:'country' },
	{ name:'Australia', type:'continent'},
	{ name:'Commonwealth of Australia', type:'country'},
	{ name:'Europe', type:'continent'},
	{ name:'Germany', type:'country' },
	{ name:'France', type:'country' },
	{ name:'Spain', type:'country' },
	{ name:'Italy', type:'country' },
	{ name:'North America', type:'continent'},
	{ name:'Mexico', type:'country'},
	{ name:'Mexico City', type:'city'},
	{ name:'Guadalajara', type:'city'},
	{ name:'Canada', type:'country'},
	{ name:'Ottawa', type:'city'},
	{ name:'Toronto', type:'city'},
	{ name:'United States of America', type:'country' },
	{ name:'South America', type:'continent'},
	{ name:'Brazil', type:'country'},
	{ name:'Argentina', type:'country'}
];

console.log(_(data).chain().jsel('.type').uniq().value());
// [ 'continent', 'country', 'city' ]
console.log(_(data).chain().jsel('.type:val("city") ~ .name').sortBy(function(value) { 
			return value.charCodeAt(0)
	}).value());
// [ 'Guadalajara', 'Khartoum', 'Mombasa', 'Mexico City', 'Nairobi', 'Ottawa', 'Toronto' ]


2 commentaires:

  1. Ah oui. Ça ressemble à ce qu'on faisait avec Dilib, mais à l'époque en shell (le uniq() en particulier).

    Je ne dois pas être assez expérimenté en Javascript pour être d'accord avec le dernier exemple d'underscore: en lisant le code, j'aurais dit 10, pas 00. J'ai dû louper une marche.

    RépondreSupprimer
  2. @H_l value.substr(1, 2) appliqué à chaque ligne

    RépondreSupprimer