解析非重复父节点中的重复子元素

时间:2014-09-29 17:49:43

标签: xml xslt xslt-1.0 xslt-2.0

我有以下输入xml,并想知道XSLT是否可以处理这种转换。如果可以的话,它怎么能实现呢?

输入xml:

<foo>
  <bar>
    <A>xxx</A>
    <B>yyy</B>
    <C>zzz</C>
    <A>aaa</A>
    <B>bbb</B>
    <C>ccc</C>
     ...
     ..
  </bar>
</foo>

输出xml:

<data>
   <A>xxx</A>
   <B>yyy</B>
   <C>zzz</C>
</data>
<data>
   <A>aaa</A>
   <B>bbb</B>
   <C>ccc</C>
</data>
....

上面的示例中可能有更多重复的A,B,C节点。由于重复不在重复的父节点中,因此不可能使用for-each。我正在探索for-each-group的选项,但不确定这是否适用。非常感谢任何建议。

2 个答案:

答案 0 :(得分:0)

使用XSLT 1.0,以下解决方案正在运行 - 使用包含6个元素的示例XML进行测试:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/*">
 <xsl:apply-templates select="/foo/bar"/>
</xsl:template>

<xsl:template match="bar">
 <xsl:for-each select="*/node()">
   <xsl:if test="(position()-1) mod(3) = 0">
    <data>
     <xsl:call-template  name="grouping">
      <xsl:with-param name="position" select="position()"/>
      <xsl:with-param name="amount" select="3"/>
     </xsl:call-template>
    </data>
   </xsl:if>
  </xsl:for-each>
</xsl:template>

<xsl:template  name="grouping">
<xsl:param name="position" select="0"/>
<xsl:param name="amount" select="0"/>
<xsl:copy-of select="//bar/*[$position]"/>
   <xsl:if test="$amount > $position or   ($position mod(3) > 0)">
    <xsl:call-template  name="grouping">
      <xsl:with-param name="position" select="$position + 1"/>
      <xsl:with-param name="amount" select="$amount"/>
    </xsl:call-template>
   </xsl:if>
 </xsl:template>
</xsl:stylesheet>

结果:

<?xml version="1.0" encoding="UTF-8"?>
<data>
   <A>xxx</A>
   <B>yyy</B>
   <C>zzz</C>
</data>
<data>
   <A>aaa</A>
   <B>bbb</B>
   <C>ccc</C>
</data>

简短解释 - 在模板匹配&#34; bar&#34;所有元素节点都在

中处理
<xsl:for-each select="*/node()">

通过使用<xsl:if test="(position()-1) mod(3) = 0">(第一个节点和每个第三个节点为0),名为&#34的模板;分组&#34;被调用以提供3个节点的生成组。使用参数position(当前节点的位置)和amount - 要在每个组中复制的节点数量调用此模板。
分组模板复制当前节点 - <xsl:copy-of select="//bar/*[$position]"/> - 检查是否已生成3个节点 - <xsl:if test="$amount > $position or ($position mod(3) > 0)"> - 并再次调用自身,以计数器的位置递增,因此将复制下一个节点。登记/> 万一它不明显 - 将为$amount > $position调用分组模板,因此前3个节点将被复制,而$position mod(3) > 0用于所有后续节点,只要它为&#39} ; s不是3的倍数。如果有例如xml中的第7个元素,它将被复制为data - 组中的单个元素。

答案 1 :(得分:0)

就像你原先想的那样,xsl:for-each-group会是完美的;使用group-starting-with属性。

XML输入

<foo>
    <bar>
        <A>xxx</A>
        <B>yyy</B>
        <C>zzz</C>
        <A>aaa</A>
        <B>bbb</B>
        <C>ccc</C>
        <A>111</A>
        <B>222</B>
        <C>333</C>
    </bar>
</foo>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="/*">
        <xsl:for-each-group select="bar/*" group-starting-with="A">
            <data>
                <xsl:apply-templates select="current-group()"/>
            </data>
        </xsl:for-each-group>       
    </xsl:template>

</xsl:stylesheet>

XML输出(根据原始问题中指定的格式不正确)

<data>
   <A>xxx</A>
   <B>yyy</B>
   <C>zzz</C>
</data>
<data>
   <A>aaa</A>
   <B>bbb</B>
   <C>ccc</C>
</data>
<data>
   <A>111</A>
   <B>222</B>
   <C>333</C>
</data>