it-swarm.dev

xml2js: jak je výstup?

Snažím se použít modul node.js xml2js

Můj kód je velmi jednoduchý:

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
        });
    });
}

Můj soubor XML je:

<?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>

Výstup mi dává:

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

Podařilo se mi zpřístupnit tělo tím, že se pokusím, ale teď jsem uvízl, je tam šablona nebo příklad toho, jak xml2js někde vypíše analyzovaný xml?

21
DrakaSAN

Pro ty, kteří se zajímají, xml2js použití a zneužívání pole

Pro můj soubor bude strom:

.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
DrakaSAN

xml2js má neochvějný úkol: převést XML na JSON způsobem, který může být obrácen, aniž by předem věděl o schématu. Zdá se být zřejmé, zpočátku:

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

Snad tak daleko, že? A co takhle?

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

Odstranění lidsky přátelských jmén pohání domov nejistotu, kterou čelí xml2js. Zpočátku si můžete myslet, že je to docela rozumné: 

{ x: { y: "z" } }

Později si přejete tento text XML a uvědomíte si, že vaše uhodnuté schéma bylo špatné:

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

Uh oh. Možná bychom měli použít pole. Alespoň všichni členové mají stejnou značku:

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

Nevyhnutelně, nicméně, to dopadá být krátkozraký:\t

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

Uh ...

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

... a pak vás někdo vyleští s některými atributy a jmennými prostory XML. 

Způsob, jak vytvořit výstižnější výstupní schéma, je pro vás zřejmý. Podrobnosti můžete odvodit z názvů značek a atributů. Rozumíte tomu. 

Knihovna toto porozumění nesdílí. 

Pokud knihovna toto schéma nezná, musí použít pole "použití a zneužití", další vrstvy objektů, názvy speciálních atributů nebo všechny tři. 

Jedinou alternativou je použití variabilního výstupního schématu. Zpočátku je to jednoduché, jak jsme viděli výše, ale rychle se ocitnete v psaní velkého množství podmíněného kódu. Zvažte, co se stane, když se děti se stejným názvem značky sbalí do seznamu, ale pouze v případě, že existuje více než jeden:

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: je to těžší, než vypadá. Přečtěte si Open311 JSON a XML konverzi strana pro podrobnosti o dalších reprezentacích na straně JSON. Všechna pole "použití a zneužití", další vrstvy objektů, členy s názvy, které se neobjevily v původním XML, nebo všechny tři. 

47
Garth Kidd

Jak dokumentace xml2js uvádí , můžete konfigurovat analyzátor tak, aby nezneužil pole, nastavením vlastnosti explicitArray na false (důležité: musí to být booleovská hodnota jako řetězec "false" bude jen ne práce) !)

Příklad:

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

Tímto způsobem byste měli mít přístup ke svým vlastnostem JSON mnohem snadněji. Doufám, že to každému pomůže.

36
Clint Eastwood

JSON, který se vrací, není příliš přátelský JavaScript. Napsal jsem pomocnou funkci, se kterou lze snadněji pracovat.

Před použitím si ho přečtěte, abyste pochopili, co to dělá.

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;
};

Příklady:

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

se stává

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

a

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

se stává

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

Bude také bezpečně převést celá čísla/plovoucí body.

Edit: Nahrazeno za ... v za

6
Tristian

Můžete zkusit soubor console.log(util.inspect(result, false, null)), který by měl zobrazit celý výsledek.

1
Stucco

Pro mě to byl problém console.dir nebo přesněji problém.

Měl jsem stejný výsledek, když jsem výstup konzole.dir:

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

Byl jsem ale překvapen, když jsem zjistil, že jde o omezení konzole a data tam byla. Zřejmě console.dir nezobrazuje více než několik úrovní. Když jsem console.dir hlubší úroveň, data tam byla:

 console.log(result.RelativeLayout.TextView);

výstup:

 { '$':
 { '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!' } }

Začal jsem hledat jiné libs, abych se vrátil a zkusil to znovu. Jestli to pomůže komukoliv hurá.

0
Guy