2013-09-26 69 views
2

我在XML片段的XSLT轉換時遇到了問題。源XML看起來像這樣:XSLT - 棘手變換

<XXX> 
    <Name>Sample</Name> 
    <MMM> 
     <AAA ID="A"/> 
     <MMM> 
      <BBB ID="B"/> 
      <MMM> 
       <AA ID="C"/> 
       <BB ID="D"/> 
      </MMM> 
     </MMM> 
    </MMM> 
</XXX> 

但需要轉化爲:

<XXX> 
    <Name>Sample</Name> 
    <MMM> 
     <MMM> 
      <MMM> 
       <AAA ID="A"/> 
       <BBB ID="B"/> 
      </MMM> 
      <AA ID="C"/>    
     </MMM> 
     <BB ID="D"/> 
    </MMM> 
</XXX> 

規則很簡單,在MMM元素只能有兩個子元素節點。如果這些節點中只有一個碰巧是另一個MMM,則需要佔據第一個位置。

使用代碼很容易,但這些XML片段是SQL數據庫中XML列的值,我想使用SQL和XSLT一起更新這些值。

任何指針或建議?

+0

您定義規則的方式不會反映在您的輸出XML中。我怎麼知道你的規則,它看起來像: ' 樣品 ' –

+0

沒錯,但生成的XML是預期目標,AAA和BBB應該像第一個那樣保留它們的遍歷順序。 – Raybiez

+0

你不僅僅在切換節點,你還將它們移動到更低或更高的層次,比如從2級到4級的AAA,以及從4級到3級的AA ......這就是我的煩惱 –

回答

1

解決此問題的一種方法是首先將MMM樹和另一方面的其他節點提取到單獨的結構中,然後再次合併它們。

這真是一個不錯的挑戰!讓我熬到凌晨4點。

下面的XSLT(幾乎見下文!)做工作:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" exclude-result-prefixes="exslt"> 
    <xsl:output method="xml" encoding="ISO-8859-1" /> 
    <!-- handling of MMM extraction --> 
    <xsl:template match="MMM" mode="extract_mmm"> 
     <MMM> 
     <xsl:apply-templates mode="extract_mmm" /> 
     </MMM> 
    </xsl:template> 
    <xsl:template match="*" mode="extract_mmm" /> 
    <!-- handling of extraction of other nodes --> 
    <xsl:template match="MMM" mode="extract_other"> 
     <xsl:apply-templates mode="extract_other" /> 
    </xsl:template> 
    <xsl:template match="*" mode="extract_other"> 
     <xsl:copy-of select="." /> 
    </xsl:template> 
    <!-- handling of merging the two partial result sets --> 
    <xsl:template match="MMM" mode="dump"> 
     <xsl:param name="others" /> 
     <xsl:choose> 
     <!-- this handles the case of an MMM being a leaf node --> 
     <xsl:when test="count(MMM) = 0"> 
      <xsl:variable name="nodes_in_next_sibling" select="2*count(following-sibling::MMM)" /> 
      <MMM> 
       <xsl:copy-of select="$others[count($others) - $nodes_in_next_sibling - 1]" /> 
       <xsl:copy-of select="$others[count($others) - $nodes_in_next_sibling]" /> 
      </MMM> 
     </xsl:when> 
     <!-- this handles the case of an inner MMM with a sibling --> 
     <xsl:when test="count(../MMM) = 2"> 
      <xsl:variable name="free_positions_in_second_child" select="count(MMM[position() = 2 and count(MMM) = 0])*2 + count(MMM[2]//MMM[count(MMM) = 0])*2 + count(MMM[position() = 2 and count(MMM) = 1]) + count(MMM[2]//MMM[count(MMM) = 1])" /> 
      <MMM> 
       <xsl:apply-templates mode="dump" select="MMM[1]"> 
        <xsl:with-param name="others" select="$others[position() &lt; count($others)- $free_positions_in_second_child + 1]" /> 
       </xsl:apply-templates> 
       <xsl:apply-templates mode="dump" select="MMM[2]"> 
        <xsl:with-param name="others" select="$others[position() &gt;= count($others) - $free_positions_in_second_child + 1]" /> 
       </xsl:apply-templates> 
      </MMM> 
     </xsl:when> 
     <!-- this handles the case of an inner MMM without sibling --> 
     <xsl:when test="count(../MMM) = 1"> 
      <MMM> 
       <xsl:apply-templates mode="dump"> 
        <xsl:with-param name="others" select="$others[position() &lt; count($others)]" /> 
       </xsl:apply-templates> 
      </MMM> 
      <xsl:copy-of select="$others[position() = count($others)]" /> 
     </xsl:when> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template match="XXX"> 
     <XXX> 
     <xsl:copy-of select="Name" /> 
     <xsl:variable name="mmm_structure"> 
      <xsl:apply-templates mode="extract_mmm" select="MMM" /> 
     </xsl:variable> 
     <xsl:variable name="other_structure_tmp"> 
      <xsl:apply-templates mode="extract_other" select="MMM" /> 
     </xsl:variable> 
     <!-- http://stackoverflow.com/questions/4610921/how-to-concatenate-two-node-sets-such-that-order-is-respected --> 
     <!-- http://www.exslt.org/exsl/ --> 
     <xsl:variable name="other_structure" select="exslt:node-set($other_structure_tmp/*)" /> 
     <xsl:apply-templates select="$mmm_structure" mode="dump"> 
      <xsl:with-param name="others" select="$other_structure" /> 
     </xsl:apply-templates> 
     </XXX> 
    </xsl:template> 
</xsl:stylesheet> 

注:

  • 這仍然是相當多的XSLT 1.0 EXSLT擴展。
  • MMM有兩個孩子的節點也被處理。將其他節點的規則是這樣的:從底部到頂部填充位置。
  • 在此版本中,其他節點儘可能向下移動而不違反2個最大子規則。所以輸出結果並不完全如問題所示,而是如下所示。

這是輸出:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<XXX> 
    <Name>Sample</Name> 
    <MMM> 
    <MMM> 
     <MMM> 
     <AAA ID="A"/> 
     <AAA ID="B"/> 
     </MMM> 
    </MMM> 
    <BBB ID="C"/> 
    </MMM> 
    <BBB ID="D"/> 
</XXX> 

如果這是一個問題,讓我知道。

+1

XSLT中的一些'count'表達式可以被簡化。請參閱http://stackoverflow.com/questions/19618479/concise-expression-for-counting-leaf-nodes-in-specific-subtrees。 –

+1

我剛剛注意到我的第三個音符(見上面)實際上確實需要注意,因爲我留下了一個不應該存在的空隙(在'B'和'C'之間關閉'MMM'之後,必須查看這個.. –

+0

儘管由於架構改變我不得不放棄這種方法,但我將這個項目保留一段時間,因爲它似乎是一個很好的挑戰,給了我所有的東西來學習,稍後我會發布我的嘗試。爲您的輸入! – Raybiez