问题描述
我们收到一个价格变化的 XML 数据包,然后想要更新 HTML 文档的特定部分。问题是我们可以看到它工作的唯一方法是通过 2 阶段转换,首先将 XML 数据包转换为格式良好的 HTML 块,然后使用第二个 XSLT 读取 HTML 文件并覆盖该特定部分。
要更新的 HTML 文件(格式正确):
<html>
<head>
<title>Mini-me Amazon</title>
</head>
<body>
<p>This is our Product Price Sheet</p>
<table style="width:100%">
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr data-key="1">
<td>Whiz-bang widget</td>
<td name="price1">$19.99</td>
</tr>
<tr data-key="3">
<td>Unreal widget</td>
<td name="price3">$99.99</td>
</tr>
...
</table>
</body>
</html>
传入的 XML:
<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet type="text/xsl" href="xml-price.xsl"?>
<supplier>
<product>
<key>3</key>
<pprice uptype="1">
<price>$22.34</price>
</pprice>
</product>
</supplier>
第一个 XSL:
<xsl:stylesheet ...>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/supplier">
<xsl:apply-templates select="product"/>
</xsl:template>
<xsl:template match="product">
<xsl:variable name="PKey">
<xsl:value-of select="key"/>
</xsl:variable>
<xsl:for-each select="pprice"> <!-- Could be more than 1 -->
<xsl:choose>
<xsl:when test="@uptype=0">
</xsl:when>
<xsl:when test="@uptype=1">
<xsl:apply-templates select="price"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template match="price">
<td name="rate$PKey"><xsl:value-of select="."/></td>
</xsl:template>
</xsl:stylesheet>
所以 Saxon-js 返回一个 <td name="price3">$22.34</td>
。都好。所以我们想要获取这个 HTML 块并更新 HTML。
第二个 XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="td[@name='price3']"> <!-- Problem 1 -->
<td name="price3">$22.34</td> <!-- Problem 2 -->
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="document('/home/tireduser/node/bigstuff/public/update-html.html')/node()"/>
</xsl:template>
</xsl:stylesheet>
问题:
我们如何将 price3
和 <td name="price3">$22.34</td>
的动态值(它们更改传入的每个新 XML)放入第二个 XSL 中,而无需将 XSL 重新编译为 Saxon- Node.js 需要并且不使用参数来传入这些值(因为我们已经读过不推荐使用参数?
或者所有这些都可以在 1 次转换中完成?
第二个问题:Saxon-js 文档说明:
Using fn:transform()
If a source XSLT stylesheet is supplied as input to the fn:transform() function in XPath,the XX compiler will be invoked to compile the stylesheet before it is executed. However,there is no way of capturing the intermediate SEF stylesheet for subsequent re-use.
我们发现这不是真的(或者做错了)。如果我们只是将 XSL 传递给 Transform 函数 (stylesheetFileName:),则会产生错误。
解决方法
我认为您基本上想要一个与
类似的样式表<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:param name="price-data">
<supplier>
<product>
<key>3</key>
<pprice uptype="1">
<price>$22.34</price>
</pprice>
</product>
</supplier>
</xsl:param>
<xsl:key name="price" match="product/pprice[@uptype = 1]/price" use="'price' || ancestor::product/key"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="td[@name][key('price',@name,$price-data)]/text()">{key('price',../@name,$price-data)}</xsl:template>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
<xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:template>
</xsl:stylesheet>
Here is an online sample 在浏览器中使用 Saxon-JS 2
为了紧凑起见,我在参数中内联了辅助数据,对于 Saxon-JS 2 和 Node.js,您基本上可以声明参数绑定一个值,例如<xsl:param name="price-data" select="doc('sample2.xml')"/>
或者您可以使用 getResource
预加载文档,然后在运行转换之前将参数设置为预加载的文档;请参阅 https://www.saxonica.com/saxon-js/documentation/index.html#!api/getResource 中的示例部分。
在您的评论中,您说您将 XML 数据作为字符串从 Node.js 传递到 Saxon-JS,在这种情况下,您需要使用 parse-xml
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:param name="price-data" as="xs:string"><![CDATA[
<supplier>
<product>
<key>3</key>
<pprice uptype="1">
<price>$22.34</price>
</pprice>
</product>
</supplier>
]]></xsl:param>
<xsl:param name="price-doc" select="parse-xml($price-data)"/>
<xsl:key name="price" match="product/pprice[@uptype = 1]/price" use="'price' || ancestor::product/key"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="td[@name][key('price',$price-doc)]/text()">{key('price',$price-doc)}</xsl:template>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
<xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:template>
</xsl:stylesheet>