我正在尝试使用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
我通过尝试成功访问了body,但现在我卡住了,是否有一个模板或xml2js如何在某处输出解析的xml的示例?
答案 0 :(得分:50)
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 and XML Conversion页面,了解其他JSON端表示的详细信息。所有“使用和滥用”数组,额外的对象层,名称未出现在原始XML中的成员,或全部三个。
答案 1 :(得分:39)
作为xml2js' documentation states,您可以通过将属性explicitArray
设置为false
来将解析器配置为不滥用数组(重要的是:它必须是一个布尔值作为字符串{ {1}}只会不工作!)
示例:
"false"
这样,您应该能够以更简单的方式访问JSON属性。我希望这对任何人都有帮助。
答案 2 :(得分:6)
回来的JSON不太友好。我编写了一个帮助函数,可以更容易使用。
请务必在使用前阅读,以便了解它的作用。
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 for for
答案 3 :(得分:4)
对于那些想知道的人,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
答案 4 :(得分:1)
您可能想尝试console.log(util.inspect(result, false, null))
,这应显示整个结果。
答案 5 :(得分:0)
对我来说,这是一个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!' } }
我开始寻找其他的libs只是为了回去再试一次。如果它有助于任何人欢呼。