如何使用 saxonjs 和 nodejs 获取 xml 文件中的变量
我有一个 xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE agent SYSTEM "http://www.someUrl.com">
<myData>
<service>
<description>Description</description>
</service>
<courier>
<listener port="55556"
/>
<mainService
name="Some Name"
port="55555"
/>
</courier>
</myData>
我想使用 nodejs 获取侦听器端口变量的值。 根据 SaxonJs 的文档, SaxonJS.XPath.evaluate 是我需要的方法。它需要一个查询和一个文档节点对象。当我将 xml 文件转换为 dom 对象时,我收到错误消息:“Uncaught TypeError TypeError:无法读取未定义的属性(读取‘length’)”
const saxonJs = require('saxon-js');
const fs = require('node:fs');
const jsdom = require("jsdom");
var xmlFile = fs.readFileSync(xmlFilePath, 'utf8');
var doc = new jsdom.JSDOM(xml);
var xpathQuery = '//listener/@port';
var result = saxonJs.XPath.evaluate(xpathQuery, doc);
举一个使用 Node.js 的 SaxonJS 的例子:
const SaxonJS = require("saxon-js");
SaxonJS.getResource({ file: 'input-sample.xml', type: 'xml' })
.then(doc => { console.log(SaxonJS.XPath.evaluate('//listener/@port/xs:integer(.)', doc)) });
当然,您也可以尝试选择
@port
属性节点并输出其值,例如
const SaxonJS = require("saxon-js");
SaxonJS.getResource({ file: 'saxon-js-input-sample1.xml', type: 'xml' })
.then(doc => { var port = SaxonJS.XPath.evaluate('//listener/@port', doc); if (port !=null) console.log(port.value) });
或者在 ESM 模块世界中输出为
import SaxonJS from 'saxon-js';
const doc = await SaxonJS.getResource({ file: 'saxon-js-input-sample1.xml', type: 'xml' });
var port = SaxonJS.XPath.evaluate('//listener/@port', doc);
if (port !=null)
console.log(port.value);
SaxonJS 不支持所有 DOM 实现。在浏览器上,它仅适用于本机浏览器实现;在 node.js 中,它仅适用于自己的 DOM 实现,即对 xmldom 进行修改并修复了一些错误。
如果您想利用 SaxonJS 中的 XPath 3.1 功能,则需要使用 SaxonJS 嵌入式 DOM 实现。另一方面,如果您只需要 XPath 1.0,那么大多数 DOM 实现都提供此功能,尽管并不总是具有高水平的一致性或性能。
您需要特别使用 XPath 吗?由于您有 JSDOM,我只需使用 CSS 选择器进行查询:
import {JSDOM} from "jsdom"; // ^24.0.0
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE agent SYSTEM "http://www.someUrl.com">
<myData>
<service>
<description>Description</description>
</service>
<courier>
<listener port="55556"
/>
<mainService
name="Some Name"
port="55555"
/>
</courier>
</myData>`;
const dom = new JSDOM(xml, {contentType: "application/xml"});
const {document} = dom.window;
const port = document.querySelector("listener").getAttribute("port");
console.log(port); // => 55556
您可以使用
JSDOM.fromFile
从磁盘读取,而无需使用
fs
(尽管这样也可以)。
如果您不介意使用 CSS 查询,Cheerio 提供了一个更简洁的选项:
const cheerio = require("cheerio"); // ^1.0.0-rc.12
const xml = `...`;
console.log(cheerio.load(xml, {xml: true})("listener").attr("port"));
如果您确实想使用 XPath,您可以在 JSDOM 中执行此操作(请注意
[@port]
更改为在
listener
元素上按属性选择):
const port = document
.evaluate(
"//listener[@port]",
document,
null,
9, // FIRST_ORDERED_NODE_TYPE
)
.singleNodeValue
.getAttribute("port");
或直接选择属性字符串值:
const port = document
.evaluate(
"//listener/@port",
document,
null,
2, // STRING_TYPE
)
.stringValue;
请注意同时使用 JSDOM 和 Saxon 可能没有必要,因为 Saxon 无需 JSDOM 支持即可解析 XML 。选择其中一个。
我不熟悉 Saxon,但在撰写本文时,JSDOM 每周下载量为 22,000,000 次,SO 上有 831 个问题,而 saxon-js 每周下载量为 20,000 次,SO 上有 61 个问题。我的直觉是尽可能避免对 saxon-js 的依赖,因为小的软件包往往无人维护,而且如果遇到问题,很难在 Stack Overflow 和 GitHub 上找到支持。
另一方面,显然 JSDOM XPath 有 “相当多的问题” ,而且 JSDOM(可能)是一个更复杂的库,所以如果 Saxon 是你特定用例的更好工具(即你需要强大的 XML 支持,不关心整个 DOM API,也不介意使用不太流行的库的支持/维护成本),那么你可能有充分的理由追求 Saxon。