如何使用 xslt 更改单个标签?

Posted

技术标签:

【中文标题】如何使用 xslt 更改单个标签?【英文标题】:How to change a single tag with xslt? 【发布时间】:2020-10-09 11:21:52 【问题描述】:

在下面的结果中,OrderDetail 已替换为 baz。类似的xslt 将如何仅更改OrderDetail 的第一次出现而没有其他出现?

使用saxonb-xslt当前结果:

<?xml version="1.0" encoding="UTF-8"?>
<Order OrderID="10613">
  <CustomerID>HILAA</CustomerID>
  <EmployeeID>4</EmployeeID>
  <OrderDate>1997-07-29T05:38:16</OrderDate>
  <RequiredDate>1997-08-26T11:38:15</RequiredDate>
  <ShippedDate>1997-08-01T12:46:12</ShippedDate>
  <ShipVia>2</ShipVia>
  <Freight>8.1100</Freight>
  <ShipName>HILARION-Abastos</ShipName>
  <ShipAddress>Carrera 22 con Ave. Carlos Soublette #8-35</ShipAddress>
  <ShipCity>San Cristóbal</ShipCity>
  <ShipRegion>Táchira</ShipRegion>
  <ShipPostalCode>5022</ShipPostalCode>
  <ShipCountry>Venezuela</ShipCountry>
  <OrderDetails>
      <baz>
         <ProductID>13</ProductID>
         <UnitPrice>6.0000</UnitPrice>
         <Quantity>8</Quantity>
         <Discount>0.1</Discount>
      </baz>
      <baz>
         <ProductID>75</ProductID>
         <UnitPrice>7.7500</UnitPrice>
         <Quantity>40</Quantity>
         <Discount>0</Discount>
      </baz>
  </OrderDetails>
</Order>

来自这个snippet 的一个更大的xml 文件:

<Order OrderID="10613">
  <CustomerID>HILAA</CustomerID>
  <EmployeeID>4</EmployeeID>
  <OrderDate>1997-07-29T05:38:16</OrderDate>
  <RequiredDate>1997-08-26T11:38:15</RequiredDate>
  <ShippedDate>1997-08-01T12:46:12</ShippedDate>
  <ShipVia>2</ShipVia>
  <Freight>8.1100</Freight>
  <ShipName>HILARION-Abastos</ShipName>
  <ShipAddress>Carrera 22 con Ave. Carlos Soublette #8-35</ShipAddress>
  <ShipCity>San Cristóbal</ShipCity>
  <ShipRegion>Táchira</ShipRegion>
  <ShipPostalCode>5022</ShipPostalCode>
  <ShipCountry>Venezuela</ShipCountry>
  <OrderDetails>
    <OrderDetail>
      <ProductID>13</ProductID>
      <UnitPrice>6.0000</UnitPrice>
      <Quantity>8</Quantity>
      <Discount>0.1</Discount>
    </OrderDetail>
    <OrderDetail>
      <ProductID>75</ProductID>
      <UnitPrice>7.7500</UnitPrice>
      <Quantity>40</Quantity>
      <Discount>0</Discount>
    </OrderDetail>
  </OrderDetails>
</Order>

使用此xslt 转换:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output indent="yes"/>


<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="OrderDetail">
    <baz>
        <xsl:apply-templates/>
    </baz>
</xsl:template>

</xsl:stylesheet>

但是,这会更改所有匹配的节点名称。

【问题讨论】:

【参考方案1】:

第一个&lt;OrderDetail&gt; 是前面没有&lt;OrderDetail&gt;s 的那个。

<xsl:template match="OrderDetail[not(preceding::OrderDetail)]">
    <baz>
        <xsl:apply-templates/>
    </baz>
</xsl:template>

这匹配整个文档中的第一个,不管嵌套。

根据您如何定义“第一”,还有其他选项,例如match="OrderDetail[1]",匹配其各自父元素中的第一个

【讨论】:

match 在 xslt 3 但不是 2?而且,是的,我可能将“第一”定义为更多 OrderDetail[1] 与某种相对 xpath (?)。 @NicholasSaunders "match 在 xslt 3 中但不在 2 中?" - match 一直都在,不知道你的意思。 "sort of relative xpath (?)" 也不清楚。你能详细说明一下吗? 我的误解,然后我认为matchxslt 3 来说是新的。就OrderDetail 在树中的位置而言,这个问题有些模糊:哪个节点是它的父节点等。使用not(proceeding() 肯定是有道理的。 @Nicholas 匹配表达式可以指定树中的位置,但它不需要。如果只给出match="OrderDetail[1]",则匹配任何元素中的任何第一个&lt;OrderDetail&gt;。如果给定"OrderDetails/OrderDetail[1]",则匹配任何&lt;OrderDetails&gt; 中的任何第一个&lt;OrderDetail&gt;,但它不会假设&lt;OrderDetails&gt; 的位置。等等。这确实与其他地方的 XPath 不同,后者总是引用上下文元素,除非它是绝对路径。

以上是关于如何使用 xslt 更改单个标签?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 XSLT 将单个子 xml 元素转换为 Json 数组

在xslt中的单个标签下解析多个标签的文本

XSLT 如何从不同的标签级别访问属性?

如何使用 XSLT 替换 XML 节点名称中的字符 - 更改根元素

XSLT 2.0 如何更改数字的默认格式?

如何在 XSLT 中的特定树层次结构中选择标签?