处理 foreach 中排除的元素

Handle excluded element in a foreach

在第一个模板中,我有意排除了一个元素('milk'),因为解析后的数据映射相对平坦,我想使用 XSLT 对数据进行分类和结构化。目的是处理第二个模板中排除的元素('milk')。这两个模板一次 运行 它们一个。 运行 模板一起将不会显示应设置另一个属性名称和属性值的排除元素 ('milk') 的结果。

JSON:

<data>
{
  "storage": {
    "pencils": 12,
    "milk": 8,
    "rulers": 4
  }
}
</data>

XSL:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:transform
  version="3.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:storage="http://www.exammple.com/1"
  xmlns:office="http://www.exammple.com/2"
  xmlns:item="http://www.exammple.com/3"
  expand-text="yes">

  <xsl:output method="xml" indent="yes"/>

  <xsl:mode on-no-match="shallow-skip"/>

  <!-- Parse JSON to XML -->

  <xsl:template match="data">
    <storage:one>
      <xsl:apply-templates select="json-to-xml(.)"/>
    </storage:one>
  </xsl:template>

  <!-- Print map -->
  <!-- <xsl:template match="*[@key = 'storage']"> <xsl:copy-of select=".."/> </xsl:template> -->

  <xsl:template match="*[@key='storage']">

      <xsl:for-each select="*[not(@key='milk')]">
      <xsl:element name="item:{@key}">
        <xsl:attribute name="office">plant-1</xsl:attribute>
        <xsl:value-of select="text()"/>
      </xsl:element>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="*[@key='milk']">
      <xsl:for-each select=".">
      <xsl:element name="item:{@key}">
        <xsl:attribute name="beverage">plant-2</xsl:attribute>
        <xsl:value-of select="text()"/>
      </xsl:element>
    </xsl:for-each>
  </xsl:template>

</xsl:transform>

结果:

<?xml version="1.0" encoding="UTF-8"?>
<storage:one xmlns:item="http://www.exammple.com/3"
             xmlns:office="http://www.exammple.com/2"
             xmlns:storage="http://www.exammple.com/1">
   <item:pencils office="plant-1">12</item:pencils>
   <item:rulers office="plant-1">4</item:rulers>
</storage:one>

想要的结果:

<?xml version="1.0" encoding="UTF-8"?>
<storage:one xmlns:item="http://www.exammple.com/3"
             xmlns:office="http://www.exammple.com/2"
             xmlns:storage="http://www.exammple.com/1">
   <item:pencils office="plant-1">12</item:pencils>
   <item:rulers office="plant-1">4</item:rulers>
   <item:milk beverage="plant-2">8</item:milk>
</storage:one>

我会为每种不同的输出类型编写模板,如果输出的顺序与输入的顺序不同,则在 xsl:sort 或 XPath 3.1 sort 调用中更改顺序:

  <xsl:template match="data">
    <storage:one>
      <xsl:apply-templates select="json-to-xml(.)"/>
    </storage:one>
  </xsl:template>
  
  <xsl:template match="*[@key = 'storage']">
    <xsl:apply-templates select="sort(*, (), function($el) { $el/@key = 'milk' })"/>
  </xsl:template>

  <xsl:template match="*[@key='storage']/*[not(@key='milk')]">
      <xsl:element name="item:{@key}">
        <xsl:attribute name="office">plant-1</xsl:attribute>
        <xsl:value-of select="text()"/>
      </xsl:element>
  </xsl:template>

  <xsl:template match="*[@key='storage']/*[@key='milk']">
      <xsl:element name="item:{@key}">
        <xsl:attribute name="beverage">plant-2</xsl:attribute>
        <xsl:value-of select="text()"/>
      </xsl:element>
  </xsl:template>

您的第二个模板永远不会匹配,因为它永远不会到达。所有元素都由 <xsl:template match="*[@key='storage']"> 处理 - 它没有 <xsl:apply-templates ...> 来访问更多模板。

您的第一个模板没有递归到它的子模板中。所以在第一个模板的末尾添加一个<xsl:apply-templates select="*" />

    <xsl:template match="*[@key='storage']">
      <xsl:for-each select="*[not(@key='milk')]">
        <xsl:element name="item:{@key}">
          <xsl:attribute name="office">plant-1</xsl:attribute>
          <xsl:value-of select="text()"/>
        </xsl:element>
      </xsl:for-each>
      <xsl:apply-templates select="*" />
    </xsl:template>

这将尝试在“存储”级别应用更多模板,因此匹配第二个模板。