TypeScript 解析 xml 的寫法與比較
工作上需要解析 XML 格式的資料,
找到兩個函式庫 - libxmljs、xml2js,在此紀錄使用方式與比較,供往後參考:
以下為方便比較,以相同資料來做評比:
寫法範例:
因為 rootNode 並沒有文字內容,只有子項目,但呼叫 .text() 時卻得到所有子項目的內容 (WHY???)
參考最後一行, sibling 節點有個 attribute 是 text ,內容正是節點內文 “with content!”
所以,調整一下程式:
因為我不太熟,所以研究了很久,在此紀錄處理方式:
若 key 為 ‘$’,value 是此節點的屬性;
若 key 為 ‘_’,value 是此節點的內容。
重點就是要一層層解析才能取得完整內容
注意:
如果有相同節點,則會被集中放在 value 中,
所以要另外判斷,因為很複雜,所以就沒有再繼續研究下去了…..
以上是我的心得,如果有其他函式庫建議,或是寫法的意見,
歡迎交流!
找到兩個函式庫 - libxmljs、xml2js,在此紀錄使用方式與比較,供往後參考:
以下為方便比較,以相同資料來做評比:
<rootNode> <child foo="bar"> <grandchild baz="fizbuzz">grandchild content</grandchild> </child> <sibling foo="call">with content!</sibling> </rootNode>
libxmljs
解析結果是類似 C# 的 XElement 結構,支援 XPath 查詢,可取得屬性或節點內容。寫法範例:
let xmlDoc = libxmljs.parseXmlString(this.xml); // xpath query var gchild = xmlDoc.get('//grandchild'); console.log(gchild.text()); // prints "grandchild content" let children = xmlDoc.get('//rootNode').childNodes(); // get child node var child = children[0]; console.log(child.attr('foo').value()); // get attribute // prints "bar"遞迴解析各節點:
private populateForlibxmljs(node: any, level: number) { console.log(new Array(level + 1).join('-') + node.name()); // 節點名稱 console.log(new Array(level + 1).join(' ') + "'" + node.text() + "'"); // 節點內容?? node.attrs().forEach(attr => { console.log(new Array(level + 1).join(' ') + attr.name() + '=' + attr.value()); // 屬性 }); node.childNodes().forEach(element => { this.populateForlibxmljs(element, level + 1); }); }結果:
-rootNode 'grandchild contentwith content!' --child 'grandchild content' foo=bar ---grandchild 'grandchild content' baz=fizbuzz ----text 'grandchild content' --sibling 'with content!' foo=call ---text 'with content!'可以看到第 2 行列印結果是出乎意料的,
因為 rootNode 並沒有文字內容,只有子項目,但呼叫 .text() 時卻得到所有子項目的內容 (WHY???)
參考最後一行, sibling 節點有個 attribute 是 text ,內容正是節點內文 “with content!”
所以,調整一下程式:
private populateForlibxmljs(node: any, level: number) { console.log(new Array(level + 1).join('-') + node.name()); // 節點名稱 node.attrs().forEach(attr => { console.log(new Array(level + 1).join(' ') + attr.name() + '=' + attr.value()); // 屬性 }); node.childNodes().forEach(element => { if (element.name() === 'text') { // 文字內容被當成屬性的其中一類了 console.log(new Array(level + 1).join(' ') + "'" + element.text() + "'"); } else { this.populateForlibxmljs(element, level + 1); } }); }結果:
-rootNode --child foo=bar ---grandchild baz=fizbuzz 'grandchild content' --sibling foo=call 'with content!'PERFECT!!
xml2js
解析結果是 JSON 格式物件,所以熟悉 JSON 處理的彭油可以開心使用。因為我不太熟,所以研究了很久,在此紀錄處理方式:
xml2js.parseString(this.xml, function (err, result) { console.dir(result); // 結果: // Object {rootNode: Object} // 由於 console.log 預設只會列印一層資料,要引用 util 函式庫才能看到物件完整內容 console.log(util.inspect(result, false, null)); // 結果: // { rootNode: // { child: // [ { '$': { foo: 'bar' }, // grandchild: [ { _: 'grandchild content', '$': { baz: 'fizbuzz' } } ] } ], // sibling: [ { _: 'with content!', '$': { foo: 'call' } } ] } } }遞迴解析各節點:
private populateForxml2js(node: any, level: number) { let keys = Object.keys(node); // 取得所有 key keys.forEach(key => { let value = node[key]; if (key === '$') { // key of attributes let subKeys = Object.keys(value); subKeys.forEach(element => { console.log(new Array(level).join(' ') + element + "=" + value[element]); }); } else if (key === '_') { // key of content console.log(new Array(level).join(' ') + "'" + value + "'"); } else if (typeof (value) === 'string') { console.log(new Array(level).join(' ') + key); console.log("key:" + key + " value:" + value); } else if (Array.isArray(value)) { value.forEach(element => { this.populateForxml2js(element, level + 1); }); } else if (value instanceof Object) { console.log(new Array(level).join(' ') + "-" + key); this.populateForxml2js(value, level + 1); } }); }結果:
-rootNode -child foo=bar -grandchild 'grandchild content' baz=fizbuzz -sibling 'with content!' foo=callJSON 物件的 key 會是 XML 的節點名稱;
若 key 為 ‘$’,value 是此節點的屬性;
若 key 為 ‘_’,value 是此節點的內容。
重點就是要一層層解析才能取得完整內容
注意:
如果有相同節點,則會被集中放在 value 中,
所以要另外判斷,因為很複雜,所以就沒有再繼續研究下去了…..
[結論]:
光看程式碼複雜度,libxmljs 輕鬆勝出!!以上是我的心得,如果有其他函式庫建議,或是寫法的意見,
歡迎交流!
留言