XSLT XML 到 HTML 两阶段转换 - 一定是更好的方法

XSLT XML to HTML 2-stage transformation - must be better way

我们得到一个 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">.99</td>
      </tr>
      <tr data-key="3">
        <td>Unreal widget</td>
        <td name="price3">.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>.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 returns 一个 <td name="price3">.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">.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>

问题:

我们如何在不将 XSL 重新编译为 . sef.json 哪些 Saxon-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>.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>.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', @name, $price-doc)]/text()">{key('price', ../@name, $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>