xml2js: comment est la sortie?

j'essaie d'utiliser le noeud.js module xml2js

Mon code est assez simple:

function testparse(pathname, callback) {
    var parser = require('xml2js').Parser(),
        util = require('util'),
        fs = require('fs'),
    fs.readFile(pathname, function (err, data) {
        parser.parseString(data, function(err, result) {
            console.log('Complete result:');
            console.log(util.inspect(result, {depth: null})); //Work
            console.log('Try to access element:');
            console.log(result.smil.body); //Work
            console.log(result.smil.body.update); //Undefined
        });
    });
}

Mon fichier xml:

<?xml version="1.0"?>
<smil>
    <head/>
    <body>
        <update /*some field*//>
        <stream name="name"/>
        <playlist /*some field*/>
            <video /*some field*//>
            <video /*some field*//>
            <video /*some field*//>
        </playlist>
    </body>
</smil>

La sortie de me donner:

Complete result:
{ smil:
    { head: [''],
      body:
        [ { update: [[Object]],
            stream: [[Object]],
            playlist: [[Object]] } ] } }
Try to access element:
[Object]
Undefined

j'ai réussi à accéder à body en essayant, mais maintenant je suis coincé, y a-t-il un modèle ou un exemple de comment xml2js produit le xml analysé quelque part?

20
demandé sur DrakaSAN 2013-11-27 13:29:13

6 réponses

xml2js a une tâche non enviable: convertir XML en JSON d'une manière qui peut être inversée, sans connaître le schéma à l'avance. Il semble évident, au premier abord:

<name>Fred</name> → { name: "Fred" }
<chacha /> → { chacha: null }

facile jusqu'ici, non? Comment à ce sujet, si?

<x><y>z</y><x>

Suppression de l'humain noms conviviaux conduit à la maison de l'incertitude face à xml2js. À première, vous pourriez penser que c'est tout à fait raisonnable:

{ x: { y: "z" } }

plus tard, vous trébuchez sur ce texte XML et réalisez votre deviné-dans le schéma est faux:

<x><y>z</y><y>z2</y></x>

Uh oh. Peut-être qu'on aurait dû utiliser un tableau. Au moins tous les membres ont la même étiquette:

{ x: [ "z", "z2" ] }

Inévitablement, cependant, qui s'avère être à courte vue:

<x><y>z</y><y>z2</y><m>n</m>happy</x>

Euh...

{ x: [ { y: "z" }, { y : "z2" }, { m: "n" }, "happy" ] }

... et puis quelqu'un vous polit avec quelques attributs et des espaces de noms XML.

la façon de construire un schéma de sortie plus concis vous semble évidente. Vous pouvez déduire les détails de l'étiquette et les noms d'attributs. Vous le comprenez.

la bibliothèque ne partage pas ce point de vue.

si la bibliothèque ne connaît pas le schéma, elle doit soit "utiliser et abuser" des tableaux, des couches supplémentaires d'objets, des noms d'attributs spéciaux, ou les trois.

la seule alternative est d'employer un schéma de sortie variable. Cela rend les choses simples au début, comme nous l'avons vu plus haut, mais vous vous trouverez rapidement à écrire beaucoup de code conditionnel. Examinons ce qui se passe si les enfants portant le même nom d'étiquette sont regroupés dans une liste, mais seulement s'il y en a plus d'une:

if (Array.isArray(x.y)) {
    processTheYChildren(x.y);
} else if (typeof(x.y) === 'object') {
    // only one child; construct an array on the fly because my converter didn't
    processTheYChildren([x.y]);
} else ...

TL;DR: c'est plus difficile qu'il n'y paraît. Lire L'Open311 Conversion JSON et XML pour plus de détails sur les autres JSON côté des représentations. Tous les tableaux" use and abuse", les couches supplémentaires d'objets, les membres avec des noms qui n'apparaissent pas dans le XML original, ou les trois.

44
répondu Garth Kidd 2014-02-24 01:46:42

les états de documentation de xml2js, vous pouvez configurer l'analyseur pour ne pas abuser des tableaux, en définissant la propriété explicitArrayfalse (important: il doit être une valeur booléenne que la chaîne de caractères "false" travail!)

Exemple:

var parser = new xml2js.Parser({explicitArray : false});

de cette façon, vous devriez être en mesure d'accéder à vos propriétés JSON d'une manière beaucoup plus facile. J'espère que cela aide quelqu'un.

35
répondu Clint Eastwood 2014-11-12 18:50:35

le JSON qui revient n'est pas très JavaScript. J'ai écrit une fonction d'assistance qui peuvent rendre plus facile de travailler avec.

assurez-vous de le lire avant de l'utiliser pour comprendre ce qu'il fait.

xml.parseString(xmlString, function(err, results){
    if(err) throw err

    results = cleanXML(results);
});

var cleanXML = function(xml){
    var keys = Object.keys(xml),
        o = 0, k = keys.length,
        node, value, singulars,
        l = -1, i = -1, s = -1, e = -1,
        isInt = /^-?\s*\d+$/,
        isDig = /^(-?\s*\d*\.?\d*)$/,
        radix = 10;

    for(; o < k; ++o){
        node = keys[o];

        if(xml[node] instanceof Array && xml[node].length === 1){
            xml[node] = xml[node][0];
        }

        if(xml[node] instanceof Object){
            value = Object.keys(xml[node]);

            if(value.length === 1){
                l = node.length;

                singulars = [
                    node.substring(0, l - 1),
                    node.substring(0, l - 3) + 'y'
                ];

                i = singulars.indexOf(value[0]);

                if(i !== -1){
                    xml[node] = xml[node][singulars[i]];
                }
            }
        }

        if(typeof(xml[node]) === 'object'){
            xml[node] = cleanXML(xml[node]);
        }

        if(typeof(xml[node]) === 'string'){
            value = xml[node].trim();

            if(value.match(isDig)){
                if(value.match(isInt)){
                    if(Math.abs(parseInt(value, radix)) <= Number.MAX_SAFE_INTEGER){
                        xml[node] = parseInt(value, radix);
                    }
                }else{
                    l = value.length;

                    if(l <= 15){
                        xml[node] = parseFloat(value);
                    }else{
                        for(i = 0, s = -1, e = -1; i < l && e - s <= 15; ++i){
                            if(value.charAt(i) > 0){
                                if(s === -1){
                                    s = i;
                                }else{
                                    e = i;
                                }
                            }
                        }

                        if(e - s <= 15){
                            xml[node] = parseFloat(value);
                        }
                    }
                }
            }
        }
    }

    return xml;
};

Exemples:

{
  queries: { query: [ {}, {}, {} ] }
}

devient

{
  queries: [ {}, {}, {} ]
}

et

{
  types: { type: [ {}, {}, {} ] }
}

devient

{
  types: [ {}, {}, {} ]
}

Il sera également en toute sécurité convertissez les entiers / points flottants.

Modifier: Remplacé pour... avec pour

5
répondu Tristian 2015-06-21 17:08:00

Pour ceux qui se demandent, xml2js l'utilisation et l'abus de tableau

Pour mon fichier, l'arbre serait:

.result //Object
|_.head //Array
|_.body //Array
  |_.update //Array
  | |_.$ //Object
  |   |_.fields //Strings
  |
  |_.stream //Array
  | |_.$ //Object
  |   |_.fields //Strings
  |
  |_.playlist //Array
    |_.$ //Object
      |_.fields //Strings
      |
      |_.video //Array
        |_.$ //Object
          |_.fields //Strings
4
répondu DrakaSAN 2013-11-27 09:48:21

Vous voudrez peut-être essayer console.log(util.inspect(result, false, null)), qui devrait afficher l'ensemble du résultat.

1
répondu Stucco 2018-04-16 05:24:16

Pour moi, c'était une console.question dir ou plus précisément une non-question.

j'ai eu le même résultat quand je console.dir la sortie:

{
 TextView: [ [Object] ],
 ImageView: [ [Object] ] } }

Mais j'ai été surpris de découvrir que c'était une console.dir limitation et les données étaient effectivement là. Apparemment console.dir ne montre pas plus de quelques niveaux. Quand j'ai de la console.dir un niveau plus profond, les données y était:

 console.log(result.RelativeLayout.TextView);

sortie:

 { '$':
 { 'android:layout_width': 'wrap_content',
   'android:layout_height': 'wrap_content',
   'android:layout_marginLeft': '10dp',
   'android:layout_marginTop': '10dp',
   'android:textColor': '#ffffff',
   'android:id': '@+id/textView',
   'android:text': 'Hello World!' } }

j'ai commencé à chercher d'autres libs seulement pour aller en arrière et essayer à nouveau. Si ça aide quelqu'un hourra.

0
répondu Guy 2015-12-12 02:26:48