XSLT:用另一个标签包装一些标签

时间:2017-05-23 13:21:25

标签: xml xslt

我得到了一些奇怪的XML,我必须在其他地方导入。结构就像这个例子:

<INVOICE>
  <HEADER_DATA_1>
    <HEADER_DATA_ITEM_1>foo</HEADER_DATA_ITEM_1>
    <HEADER_DATA_ITEM_2>bar</HEADER_DATA_ITEM_2>
  </HEADER_DATA_1>
  <HEADER_DATA_2>
    <HEADER_DATA_ITEM_3>foo</HEADER_DATA_ITEM_3>
    <HEADER_DATA_ITEM_4>bar</HEADER_DATA_ITEM_4>
  </HEADER_DATA_2>
  <HEADER_DATA_3>
    <HEADER_DATA_ITEM_5>foo</HEADER_DATA_ITEM_5>
    <HEADER_DATA_ITEM_6>bar</HEADER_DATA_ITEM_6>
  </HEADER_DATA_3>

  <POSITION_DATA_1>
    <POSITION_DATA_ITEM_1>foo</POSITION_DATA_ITEM_1>
    <POSITION_DATA_ITEM_2>bar</POSITION_DATA_ITEM_2>
  </POSITION_DATA_1>
  <POSITION_DATA_2>
    <POSITION_DATA_ITEM_3>foo</POSITION_DATA_ITEM_3>
    <POSITION_DATA_ITEM_4>bar</POSITION_DATA_ITEM_4>
  </POSITION_DATA_2>
  [...]
  <POSITION_DATA_8>
    <POSITION_DATA_ITEM_15>foo</POSITION_DATA_ITEM_15>
    <POSITION_DATA_ITEM_16>bar</POSITION_DATA_ITEM_16>
  </POSITION_DATA_8>

  <POSITION_DATA_1>
    <POSITION_DATA_ITEM_1>foo</POSITION_DATA_ITEM_1>
    <POSITION_DATA_ITEM_2>bar</POSITION_DATA_ITEM_2>
  </POSITION_DATA_1>
  <POSITION_DATA_2>
    <POSITION_DATA_ITEM_3>foo</POSITION_DATA_ITEM_3>
    <POSITION_DATA_ITEM_4>bar</POSITION_DATA_ITEM_4>
  </POSITION_DATA_2>
  [...]
  <POSITION_DATA_8>
    <POSITION_DATA_ITEM_15>foo</POSITION_DATA_ITEM_15>
    <POSITION_DATA_ITEM_16>bar</POSITION_DATA_ITEM_16>
  </POSITION_DATA_8>

  <POSITION_DATA_1>
    <POSITION_DATA_ITEM_1>foo</POSITION_DATA_ITEM_1>
    <POSITION_DATA_ITEM_2>bar</POSITION_DATA_ITEM_2>
  </POSITION_DATA_1>
  <POSITION_DATA_2>
    <POSITION_DATA_ITEM_3>foo</POSITION_DATA_ITEM_3>
    <POSITION_DATA_ITEM_4>bar</POSITION_DATA_ITEM_4>
  </POSITION_DATA_2>
  [...]
  <POSITION_DATA_8>
    <POSITION_DATA_ITEM_15>foo</POSITION_DATA_ITEM_15>
    <POSITION_DATA_ITEM_16>bar</POSITION_DATA_ITEM_16>
  </POSITION_DATA_8>

</INVOICE>

如您所见,发票objekt可以有一些单一的标题数据和一些多个位置,这些位置包含一些位置数据。但遗憾的是,这些出口的人忘了在位置数据周围包裹一个合适的标签。所以我想修复它,以便最终的代码看起来像这样:

<INVOICE>
  <HEADER_DATA_1>
    <HEADER_DATA_ITEM_1>foo</HEADER_DATA_ITEM_1>
    <HEADER_DATA_ITEM_2>bar</HEADER_DATA_ITEM_2>
  </HEADER_DATA_1>
  <HEADER_DATA_2>
    <HEADER_DATA_ITEM_3>foo</HEADER_DATA_ITEM_3>
    <HEADER_DATA_ITEM_4>bar</HEADER_DATA_ITEM_4>
  </HEADER_DATA_2>
  <HEADER_DATA_3>
    <HEADER_DATA_ITEM_5>foo</HEADER_DATA_ITEM_5>
    <HEADER_DATA_ITEM_6>bar</HEADER_DATA_ITEM_6>
  </HEADER_DATA_3>

  <POSITION>
    <POSITION_DATA_1>
      <POSITION_DATA_ITEM_1>foo</POSITION_DATA_ITEM_1>
      <POSITION_DATA_ITEM_2>bar</POSITION_DATA_ITEM_2>
    </POSITION_DATA_1>
    <POSITION_DATA_2>
      <POSITION_DATA_ITEM_3>foo</POSITION_DATA_ITEM_3>
      <POSITION_DATA_ITEM_4>bar</POSITION_DATA_ITEM_4>
    </POSITION_DATA_2>
    [...]
    <POSITION_DATA_8>
      <POSITION_DATA_ITEM_15>foo</POSITION_DATA_ITEM_15>
      <POSITION_DATA_ITEM_16>bar</POSITION_DATA_ITEM_16>
    </POSITION_DATA_8>
  </POSITION>

  <POSITION>
    <POSITION_DATA_1>
      <POSITION_DATA_ITEM_1>foo</POSITION_DATA_ITEM_1>
      <POSITION_DATA_ITEM_2>bar</POSITION_DATA_ITEM_2>
    </POSITION_DATA_1>
    <POSITION_DATA_2>
      <POSITION_DATA_ITEM_3>foo</POSITION_DATA_ITEM_3>
      <POSITION_DATA_ITEM_4>bar</POSITION_DATA_ITEM_4>
    </POSITION_DATA_2>
    [...]
    <POSITION_DATA_8>
      <POSITION_DATA_ITEM_15>foo</POSITION_DATA_ITEM_15>
      <POSITION_DATA_ITEM_16>bar</POSITION_DATA_ITEM_16>
    </POSITION_DATA_8>
  </POSITION>

  <POSITION>
    <POSITION_DATA_1>
      <POSITION_DATA_ITEM_1>foo</POSITION_DATA_ITEM_1>
      <POSITION_DATA_ITEM_2>bar</POSITION_DATA_ITEM_2>
    </POSITION_DATA_1>
    <POSITION_DATA_2>
      <POSITION_DATA_ITEM_3>foo</POSITION_DATA_ITEM_3>
      <POSITION_DATA_ITEM_4>bar</POSITION_DATA_ITEM_4>
    </POSITION_DATA_2>
    [...]
    <POSITION_DATA_8>
      <POSITION_DATA_ITEM_15>foo</POSITION_DATA_ITEM_15>
      <POSITION_DATA_ITEM_16>bar</POSITION_DATA_ITEM_16>
    </POSITION_DATA_8>
  </POSITION>
</INVOICE>

更糟糕的是,某些元素是可选的,尤其是POSITION_DATA_1元素。至少POSITION_DATA_2和POSITION_DATA_8是强制性的。我修复了(可能)缺少的POSITION_DATA_1标签,如下所示:

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

  <xsl:template match="POSITION_DATA_2[not(preceding-sibling::POSITION_DATA_1)]">
    <xsl:element name="POSITION_DATA_1"/>
    <xsl:call-template name="identity"/>
  </xsl:template>

如果缺少则插入它。为了我的主要目标,我尝试调整this thread的解决方案。我试过的规则看起来像这样:

<xsl:template match="INVOICE">
    <xsl:copy>
        <xsl:apply-templates select="HEADER_DATA_1|HEADER_DATA_2|HEADER_DATA_3"/>
        <POSITION>
            <xsl:apply-templates select="POSITION_DATA_1|POSITION_DATA_2|POSITION_DATA_3|POSITION_DATA_4|POSITION_DATA_5|POSITION_DATA_6|POSITION_DATA_7|POSITION_DATA_8"/>
        </POSITION>
    </xsl:copy>
</xsl:template>

但这不符合。它只包含一个POSITION标签围绕所有位置。有什么建议吗?

3 个答案:

答案 0 :(得分:0)

考虑使用密钥按前面POSITION_DATA_2

POSITION_DATA_8POSITION_DATA_1元素进行分组

                               

然后,要在所有项目周围包含一个包含标记,您只需要一个模板POSITION_DATA_1,然后使用该键来获取所有其他项目。

  <xsl:template match="POSITION_DATA_1">
    <POSITION>
      <xsl:call-template name="identity" />
      <xsl:apply-templates select="key('pos', generate-id())" />
    </POSITION>
  </xsl:template>

试试这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes" />
  <xsl:key name="pos" 
           match="*[starts-with(local-name(), 'POSITION_DATA_') and not(self::POSITION_DATA_1)]"
           use="generate-id(preceding-sibling::POSITION_DATA_1[1])" />

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

  <xsl:template match="INVOICE">
    <xsl:copy>
        <xsl:apply-templates select="HEADER_DATA_1|HEADER_DATA_2|HEADER_DATA_3"/>
        <xsl:apply-templates select="POSITION_DATA_1"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="POSITION_DATA_1">
    <POSITION>
      <xsl:call-template name="identity" />
      <xsl:apply-templates select="key('pos', generate-id())" />
    </POSITION>
  </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:0)

如果您可以使用XSLT 2.0,那么可以使用位置分组来实现:

DeletemodalComponent

答案 2 :(得分:0)

谢谢你们的好主意! 我终于改编了Michael Kays的答案。现在是时候提出我的最终解决方案了:

<xsl:template match="INVOICE">
    <xsl:for-each-group select="*" group-starting-with="POSITION_DATA_1 | POSITION_DATA_2[not(preceding-sibling::POSITION_DATA_1)]">
        <xsl:choose>
            <!-- Do no wrap the header data group -->
            <xsl:when test="starts-with(local-name(), 'HEADER_DATA_1')">
                <xsl:apply-templates select="current-group()"/>
            </xsl:when>
            <xsl:otherwise>
                <POSITION>
                    <xsl:apply-templates select="current-group()"/>
                </POSITION>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each-group>
</xsl:template>