2014-09-29 102 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