开发者问题收集

如何使用 saxonjs 和 nodejs 获取 xml 文件中的变量

2024-03-18
151

我有一个 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);
3个回答

举一个使用 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);
Martin Honnen
2024-03-18

SaxonJS 不支持所有 DOM 实现。在浏览器上,它仅适用于本机浏览器实现;在 node.js 中,它仅适用于自己的 DOM 实现,即对 xmldom 进行修改并修复了一些错误。

如果您想利用 SaxonJS 中的 XPath 3.1 功能,则需要使用 SaxonJS 嵌入式 DOM 实现。另一方面,如果您只需要 XPath 1.0,那么大多数 DOM 实现都提供此功能,尽管并不总是具有高水平的一致性或性能。

Michael Kay
2024-03-18

您需要特别使用 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。

ggorlen
2024-03-18