Xml2js: как вывод?
Я пытаюсь использовать модуль node.js xml2js
Мой код довольно прост:
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
});
});
}
Мой 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>
Результат дает мне:
Complete result:
{ smil:
{ head: [''],
body:
[ { update: [[Object]],
stream: [[Object]],
playlist: [[Object]] } ] } }
Try to access element:
[Object]
Undefined
Мне удастся получить доступ к телу, пытаясь, но теперь я застрял, есть ли шаблон или пример того, как xml2js выводит анализируемый xml где-нибудь?
Ответы
Ответ 1
Для тех, кому интересно, xml2js использует и злоупотребляет массивом
Для моего файла дерево будет выглядеть следующим образом:
.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
Ответ 2
xml2js имеет незавидную задачу: конвертируйте XML в JSON таким образом, чтобы его можно было отменить, не зная схемы заранее. Кажется очевидным, сначала:
<name>Fred</name> → { name: "Fred" }
<chacha /> → { chacha: null }
Легко до сих пор, правильно? Как насчет этого?
<x><y>z</y><x>
Удаление дружественных человеческих имен приводит к неопределенности, стоящей перед xml2js
. Сначала вы можете подумать, что это вполне разумно:
{ x: { y: "z" } }
Позже вы отправитесь на этот XML-текст и поймете, что ваша догадка - в схеме была неправильной:
<x><y>z</y><y>z2</y></x>
О, о. Возможно, мы должны были использовать массив. По крайней мере, все члены имеют один и тот же тег:
{ x: [ "z", "z2" ] }
Неизбежно, однако, оказывается близоруким:
<x><y>z</y><y>z2</y><m>n</m>happy</x>
Э...
{ x: [ { y: "z" }, { y : "z2" }, { m: "n" }, "happy" ] }
... а затем кто-то отполирует вас некоторыми атрибутами и пространствами имен XML.
Способ построения более сжатой схемы вывода кажется вам очевидным. Вы можете вывести данные из тегов и имен атрибутов. Вы это понимаете.
Библиотека не разделяет это понимание.
Если библиотека не знает схему, она должна либо "использовать и злоупотреблять" массивами, либо дополнительными слоями объектов, либо специальными именами атрибутов, либо всеми тремя.
Единственная альтернатива - использовать схему с переменными выводами. Это сначала упрощает его, как мы видели выше, но вы быстро обнаружите, что пишете много условного кода. Посмотрите, что произойдет, если дети с одинаковым именем тега рухнули в список, но только если их несколько:
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: это сложнее, чем кажется. Прочтите страницу Open311 JSON и XML Conversion для получения подробной информации о других представлениях JSON. Все массивы "использования и злоупотребления", дополнительные слои объектов, члены с именами, которые не отображаются в исходном XML, или все три.
Ответ 3
Как xml2js 'documentation states, вы можете настроить синтаксический анализатор, чтобы не злоупотреблять массивами, установив свойство explicitArray
на false
(важно: оно должно быть логическим значением, так как строка "false"
будет работать только не!)
Пример:
var parser = new xml2js.Parser({explicitArray : false});
Таким образом, вы сможете получить доступ к своим свойствам JSON намного проще. Надеюсь, это поможет любому.
Ответ 4
JSON, который возвращается, не слишком дружелюбен JavaScript. Я написал вспомогательную функцию, облегчающую работу с ней.
Обязательно прочитайте его, прежде чем использовать его, чтобы вы поняли, что он делает.
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;
};
Примеры:
{
queries: { query: [ {}, {}, {} ] }
}
становится
{
queries: [ {}, {}, {} ]
}
и
{
types: { type: [ {}, {}, {} ] }
}
становится
{
types: [ {}, {}, {} ]
}
Он также будет безопасно преобразовывать целые числа/плавающие точки.
Изменить: Заменено для... in с для
Ответ 5
Для меня это была проблема console.dir или, точнее, не проблема.
У меня был тот же результат, когда я console.dir вывод:
{
TextView: [ [Object] ],
ImageView: [ [Object] ] } }
Но я был удивлен, узнав, что это ограничение console.dir и данные на самом деле там. По-видимому console.dir не показывает больше, чем несколько уровней. Когда я console.dir более глубокий уровень данных были:
console.log(result.RelativeLayout.TextView);
выход:
{ '$':
{ '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!' } }
Я начал искать другие библиотеки только для того, чтобы вернуться и повторить попытку. Если это помогает кому-то ура.
Ответ 6
Возможно, я не понимаю вопроса, но думаю, что вы можете попробовать console.log(util.inspect(result, false, null))
, который должен отображать весь результат.