2014-09-29 90 views
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的选项,但不确定这是否适用。将不胜感激任何意见。

+0

这是*分组*问题(执行搜索)。如果您使用XSLT 1.0或2.0,解决方案会非常不同 - 请选择一个(这些标签意味着互斥)。 – 2014-09-29 18:15:18

回答

0

使用XSLT 1.0,下面的解决方案是工作 - 测试与您的示例XML 6个元素:

<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> 

作为简短的说明 - 在模板匹配 “栏中的” 所有元素节点在

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

通过使用<xsl:if test="(position()-1) mod(3) = 0">(w对于第一个节点和第三个节点,其值为0),称为“分组”的模板被称为提供3个节点的生成组。这个模板用参数位置(当前节点的位置)和数量 - 要在每个组中复制的节点数量来调用。
分组模板复制当前节点 - <xsl:copy-of select="//bar/*[$position]"/> - 检查是否已经生成了3个节点 - <xsl:if test="$amount > $position or ($position mod(3) > 0)"> - 并再次调用自身,以计数器的形式递增位置,因此下一个节点将被复制。
为防万一它不明显 - 只要不是3的倍数,分组模板将被调用$amount > $position,所以前3个节点将被复制,并且所有后续节点的$position mod(3) > 0只要不是3的倍数。在你的xml中第7个元素,这将被复制为data-group中的单个元素。

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> 
+0

感谢您的所有输入。我设法“说服”上游应用程序团队重新包装数据以实现更轻松的管理,以便下游应用程序可以更轻松地处理它。 ;) – 2014-10-07 22:36:41