2014-03-05 35 views
1

我有一個訂單行列表,每個產品都在它們上面。這些產品可能會形成一個自我引用的層次結構。我需要以這樣一種方式排序:所有沒有父母或父母在訂單中缺失的產品位於頂部,其次是子女。最終結果中,沒有一個孩子可能超過其父母。如何訂購自引用xml

那麼,如何可以訂購以下XML:

<order> 
    <line><product code="3" parent="1"/></line> 
    <line><product code="2" parent="1"/></line> 
    <line><product code="6" parent="X"/></line> 
    <line><product code="1" /></line> 
    <line><product code="4" parent="2"/></line> 
</order> 

進入這個:

<order> 
    <line><product code="6" parent="X"/></line> 
    <line><product code="1" /></line> 
    <line><product code="2" parent="1"/></line> 
    <line><product code="3" parent="1"/></line> 
    <line><product code="4" parent="2"/></line> 
</order> 

注意,特定等級內的順序並不重要,只要孩子節點如下的它是父母之後的一點。

我有一個工程層次的解決方案不超過預定深度:

<order> 

<xsl:variable name="level-0" 
    select="/order/line[ not(product/@parent=../line/product/@code) ]"/> 
<xsl:for-each select="$level-0"> 
    <xsl:copy-of select="."/> 
</xsl:for-each> 

<xsl:variable name="level-1" 
    select="/order/line[ product/@parent=$level-0/product/@code ]"/> 
<xsl:for-each select="$level-1"> 
    <xsl:copy-of select="."/> 
</xsl:for-each> 

<xsl:variable name="level-2" 
    select="/order/line[ product/@parent=$level-1/product/@code ]"/> 
<xsl:for-each select="$level-2"> 
    <xsl:copy-of select="."/> 
</xsl:for-each> 

</order> 

以上示例XSLT將爲層次工作,3個級別的最大深度,並很容易擴展到更多,但我怎麼能概括這一點,並讓xslt正確排序任意級別的深度?

+0

「*所有沒有父母或父母在訂單中缺失的產品都位於頂部,其次是子女。*」您是否想讓父母立即跟隨**,孩子,或一起列出同一代的所有項目。 –

回答

0

非常有趣的問題。我會在兩遍中做到這一點:首先,根據層次結構嵌套元素。然後輸出元素,按祖先的數量排序。

XSLT 1.0(+ EXSLT節點集()函數):

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:exsl="http://exslt.org/common" 
extension-element-prefixes="exsl"> 
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 

<xsl:key name="product-by-code" match="product" use="@code" /> 

<!-- first pass --> 
<xsl:variable name="nested"> 
    <xsl:apply-templates select="/order/line/product[not(key('product-by-code', @parent))]" mode="nest"/> 
</xsl:variable> 

<xsl:template match="product" mode="nest"> 
    <xsl:copy> 
     <xsl:copy-of select="@*"/> 
     <xsl:apply-templates select="../../line/product[@parent=current()/@code]" mode="nest"/> 
    </xsl:copy> 
</xsl:template> 

<!-- output --> 
<xsl:template match="/order"> 
    <xsl:copy> 
     <xsl:for-each select="exsl:node-set($nested)//product"> 
     <xsl:sort select="count(ancestor::*)" data-type="number" order="ascending"/> 
      <line><product><xsl:copy-of select="@*"/></product></line> 
     </xsl:for-each> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 

當適用於您的輸入,其結果是:

<?xml version="1.0" encoding="UTF-8"?> 
<order> 
    <line> 
     <product code="6" parent="X"/> 
    </line> 
    <line> 
     <product code="1"/> 
    </line> 
    <line> 
     <product code="3" parent="1"/> 
    </line> 
    <line> 
     <product code="2" parent="1"/> 
    </line> 
    <line> 
     <product code="4" parent="2"/> 
    </line> 
</order> 

這仍然留下的問題現有的/缺失的父母X - 我將盡力在稍後解決。

1

首先,你可以定義幾個鍵來幫你找了線通過它們代碼屬性元素

<xsl:key name="products-by-parent" match="line" use="product/@parent" /> 
<xsl:key name="products-by-code" match="line" use="product/@code" /> 

你會通過選擇開始元件沒有父,利用鍵來執行這個檢查:

<xsl:apply-templates select="line[not(key('products-by-code', product/@parent))]"/> 

然後,該元素相匹配的模板內,你將剛纔複製的元素,然後選擇它的「孩子」像這樣,使用等關鍵

<xsl:apply-templates select="key('products-by-parent', product/@code)"/> 

這將是一個遞歸調用,所以它會遞歸地尋找它的孩子,直到找不到更多的孩子。

試試這個XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 
    <xsl:key name="products-by-parent" match="line" use="product/@parent"/> 
    <xsl:key name="products-by-code" match="line" use="product/@code"/> 

    <xsl:template match="order"> 
     <xsl:copy> 
     <xsl:apply-templates select="line[not(key('products-by-code', product/@parent))]"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="line"> 
     <xsl:call-template name="identity"/> 
     <xsl:apply-templates select="key('products-by-parent', product/@code)"/> 
    </xsl:template> 

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

請注意使用XSLT identity transform到現有節點中的XML複製。

+0

如果輸入包含兩個或更多的祖先**和孩子**,我不認爲你會得到正確的答案。但也許我誤解了這個問題。 –