09 juillet 2010

Générer indifféremment du XML ou du JSON

PHP propose la classe XMLWriter qui permet de produire des données en XML. Par exemple le code suivant :

 
$w = new XMLWriter();
$w->openUri('php://stdout');
$w->setIndent(true);
$w->setIndentString('    ');
$w->startDocument('1.0', 'utf-8', true);
$w->writePI('xml-stylesheet', 'type="text/xsl" media="screen" href="test.xsl"');
$w->writeComment('Exemple');
$w->startElementNS('rdf', 'RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
$w->writeAttributeNS('xmlns', 'skos',  null, 'http://www.w3.org/2004/02/skos/core#');
$w->startElement('skos:Concept');
$w->writeAttribute('rdf:about', '#truc');
$w->startElement('skos:prefLabel');
$w->writeAttribute('xml:lang', 'fr');
$w->text('truc');
$w->endElement(); 
$w->endElement(); 
$w->startElement('skos:Concept');
$w->writeAttribute('rdf:about', '#bidule');
$w->startElement('skos:prefLabel');
$w->writeAttribute('xml:lang', 'fr');
$w->text('bidule');
$w->endElement();
$w->endElement();
$w->startElement('skos:Concept');
$w->writeAttribute('rdf:about', '#chouette');
$w->startElement('skos:prefLabel');
$w->writeAttribute('xml:lang', 'fr');
$w->text('chouette');
$w->endElement();
$w->endElement();
$w->endElement();
$w->endDocument();
$w->flush();

produira :

<?xml version="1.0" encoding="UTF-8" standalone="1"?>
<?xml-stylesheet type="text/xsl" media="screen" href="test.xsl"?>
<!--Example-->
<rdf:RDF xmlns:skos="http://www.w3.org/2004/02/skos/core#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <skos:Concept rdf:about="#truc">
        <skos:prefLabel xml:lang="fr">truc</skos:prefLabel>
    </skos:Concept>
    <skos:Concept rdf:about="#bidule">
        <skos:prefLabel xml:lang="fr">bidule</skos:prefLabel>
    </skos:Concept>
    <skos:Concept rdf:about="#chouette">
        <skos:prefLabel xml:lang="fr">chouette</skos:prefLabel>
    </skos:Concept>
</rdf:RDF>

Et bien voici la classe JSONWriter qui fonctionne exactement de la même manière à la différence près qu'elle produit du JSON à la place du XML. Il suffit simplement de remplacer la construction par :

require_once 'JSONWriter.php';
$w = new JSONWriter;

Ce qui permettra de générer :

{
    "version": "1.0",
    "encoding": "utf-8",
    "standalone": "yes",
    "<?xml-stylesheet": {
        "$t": "type=\"text\/xsl\" media=\"screen\" href=\"test.xsl\""
    },
    "rdf$RDF": {
        "xmlns$rdf": "http:\/\/www.w3.org\/1999\/02\/22-rdf-syntax-ns#",
        "xmlns$skos": "http:\/\/www.w3.org\/2004\/02\/skos\/core#",
        "skos:Concept": [
            {
                "rdf:about": "#truc",
                "skos:prefLabel": {
                    "xml:lang": "fr",
                    "$t": "truc"
                }
            },
            {
                "rdf:about": "#bidule",
                "skos:prefLabel": {
                    "xml:lang": "fr",
                    "$t": "bidule"
                }
            },
            {
                "rdf:about": "#chouette",
                "skos:prefLabel": {
                    "xml:lang": "fr",
                    "$t": "chouette"
                }
            }
        ]
    }
}

Mapping JSON - XML

La conversion XML vers JSON n'est pas à ma connaissance normalisée et il existe donc différente façon de faire. Si le sujet vous intéresse je vous invite à lire cet article : Converting XML to JSON. Celui-ci résume assez bien les différentes pratiques. La classe JSONWriter implémente actuellement la méthode utilisée par Google et décrite sur la page Using JSON in the Google Data Protocol

Problèmes

La méthode Google est simple, facile à comprendre mais elle est limitée. Elle n'intègre pas les Processsing Instruction ni les DTD mais surtout elle ne permet pas l'usage pour une balise d'un attribut et d'une sous balise de même nom.

La classe JSONWriter souffre également de ce défaut, elle ne traite pas non plus les DTD mais par contre elle traite parfaitement les PI en réalisant une association '<?target_of_pi' : 'valeur_pi'.

Évolution

Je ne ferai probablement pas évoluer cette classe, car elle couvre le besoin pour lequel je l'ai écrit. Mais on peut imaginer qu'elle puisse générer du Json façon Yahoo. Et que la méthode flush soit réellement active (Dans la version 1.0, les "flush" intermédiaire sont simplement ignorés).

Téléchargement et code source

Le code source est disponible sur GitHub : http://github.com/touv/JSONWriter

Ou, on peut directement l'installer avec PEAR en s'abonnant au Channel Respear :


% pear channel-discover pear.respear.net
% pear install respear/JSONWriter

3 commentaires:

  1. Merci beaucoup! Cette classe couvre mon besoin aussi!

    RépondreSupprimer
  2. Je remarque que les contenus des balises XML sont transformés en JSON vers la combinaison clé / valeur "$t" / "valeur".

    C'est pas forcément très pratique à l'usage mais j'imagine que c'est obligatoire ?

    RépondreSupprimer
  3. Obligatoire si on veut respecter la documentation google qui indique "Text values of tags are converted to $t properties." cf. http://code.google.com/apis/gdata/docs/json.html

    Maintenant, va savoir pourquoi les ingénieurs de Google ont choisi cette syntaxe...

    RépondreSupprimer