对于 NodeJs 工具,我必须使用 XSLT 执行简单的 XML 转换。
我想使用 SaxonJs,但我不想参与整个 xslt3/sef 的事情(xslt 发生变化,该工具需要在没有安装 xslt3 的计算机上运行)。
还有一种替代方法,那就是在 XPath 表达式中使用
fn:transform
函数。
虽然它似乎完全按照我想要的方式工作,但当我使用某些表达式时,我无法对结果执行任何 XPath ,因为根据 SaxonJs,根节点不是文档节点。
所以这有效:
/*[1]/*[1]
但这不是:
//*[@someAttr]/name
因为然后我收到此错误:
Exception has occurred: XError: Root node for '/' must be a document node
当我在源文档上使用第二个 XPath 表达式时,没有收到错误。
此外,当我序列化源和结果并比较它们时,它们在各个方面都是相同的(!)(除了属性值的微小变化,但这就是 XSLT 应该做的)
这是我的代码:
const applyXsltToXml = function (xmlData, transformXslt) {
const filteredXML = SaxonJS.XPath.evaluate(
`fn:transform(map { ` +
`'source-node': fn:parse-xml($xml), ` +
`'stylesheet-node': fn:parse-xml($xslt), ` +
`'delivery-format': 'raw' ` +
`})?output`,
null,
{ 'params': { 'xml': xmlData, 'xslt': transformXslt } }
);
if (_debug) {
_write(`Xml transformed using Xslt; root node "${filteredXML?.localName}"`);
}
return filteredXML;
}
在我看来,问题在于 XDM 的结果在某种程度上不正确。当我比较源文档的 XDM 和结果 XML 的结果时我看到的差异:
sourceDoc
具有 documentElement
属性sourceDoc.documentElement
也有 $d
属性,但在 transformedDoc
的 XDM 中,它是直接子级($d
包含命名空间)localName
/nodeName
/tagName
也是transformedDoc
的直接属性,而在sourceDoc
中可以在属性firstChild
这是我正在使用的 XSLT:
let xslt = `<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Filter out @cmyk -->
<xsl:template match="@cmyk"/>
</xsl:stylesheet>
`;
似乎只有一件小事出了问题,但我看不出来。我一直在查看
fn:transform
的论点,但找不到任何东西。难道和?output
有关系吗?
或者我需要以某种方式将结果转换成文档吗?
更新
感谢@martin-honnen,我现在知道问题出在
.xslt
样式表上。
我尝试了稍微不同的 xslt v1 副本,但这也不起作用。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- Filter out -->
<xsl:template match="@cmyk"/>
</xsl:stylesheet>
什么可以解释这一点?
我将为您提供一个使用 SaxonJS 2(在 Node.js 下使用 2.6 进行测试)针对 XML 示例运行与您类似的样式表(适合使用 XSLT 3 功能)的示例,以将转换结果作为 XDM 文档返回,其中随后执行 XPath 评估:
const SaxonJS = require("saxon-js");
const applyXsltToXml = function (xmlData, transformXslt) {
const filteredXML = SaxonJS.XPath.evaluate(
`transform(map {
'source-node': fn:parse-xml($xml),
'stylesheet-node': fn:parse-xml($xslt),
'delivery-format': 'raw'
})?output`,
null,
{ 'params': { 'xml': xmlData, 'xslt': transformXslt } , resultForm : 'xdm'}
)[0];
return filteredXML;
}
const xmlExample1 = `<root>
<foo>bar</foo>
<bar cmyk="...">test</bar>
</root>`;
const xsltExample1 = `<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:mode on-no-match="shallow-copy"/>
<!-- Filter out @cmyk -->
<xsl:template match="@cmyk"/>
</xsl:stylesheet>`;
const resultExample1 = applyXsltToXml(xmlExample1, xsltExample1);
console.log(resultExample1);
const result2 = SaxonJS.XPath.evaluate(`count(//@cmyk)`, resultExample1);
console.log(result2);
最终的 XPath 评估返回 0。