2015-02-10 150 views
2

我目前正在嘗試展平一個xml結構以顯示在一個簡單的表中。 基本的問題是,xml包含不同級別上的重複節點 - 提取節點的每個組合都應該導致一個單獨的輸出節點。使用XSLT展平XML樹 - 可選但重複節點

XML文檔看起來是這樣的:

<customer> 
    <name>Mustermann</name> 
    <contract> 
     <contract_id>C1</contract_id> 
     <products> 
      <product> 
       <name>Product C1.P1</name> 
       <price>23.12</price> 
       <properties> 
        <property>Property C1.P1.A</property> 
        <property>Property C1.P1.B</property> 
       </properties> 
      </product> 
      <product> 
       <name>Product C1.P2</name> 
       <price>2.32</price> 
      </product> 
     </products> 
    </contract> 
    <contract> 
     <contract_id>C2</contract_id> 
     <products> 
      <product> 
       <name>Product C2.P1</name> 
       <price>143.33</price> 
      </product> 
      <product> 
       <name>Product C2.P2</name> 
       <price>231.76</price> 
       <properties> 
        <property>Property C2.P2.A</property> 
        <property>Property C2.P2.B</property> 
       </properties> 
      </product> 
     </products> 
    </contract> 
    <contract> 
     <contract_id>C3</contract_id> 
    </contract> 
</customer> 

所以合同並不需要有產品,產品並不需要有屬性。

結果應該使重複節點變平併爲每組提取的節點形成一個單獨的結果節點(這當然會在結果中引入一些冗餘)。

輸出應該是這樣的:

<output> 
    <data> 
     <customer_name>Mustermann</customer_name> 
     <contract_id>C1</contract_id> 
     <product_name>Product C1.P1</product_name> 
     <product_price>23.12</product_price> 
     <product_property>Property C1.P1.A</product_property> 
    </data> 
    <data> 
     <customer_name>Mustermann</customer_name> 
     <contract_id>C1</contract_id> 
     <product_name>Product C1.P1</product_name> 
     <product_price>23.12</product_price> 
     <product_property>Property C1.P1.B</product_property> 
    </data> 
    <data> 
     <customer_name>Mustermann</customer_name> 
     <contract_id>C1</contract_id> 
     <product_name>Product C1.P2</product_name> 
     <product_price>2.32</product_price> 
     <product_property/> 
    </data> 
    <data> 
     <customer_name>Mustermann</customer_name> 
     <contract_id>C2</contract_id> 
     <product_name>Product C2.P1</product_name> 
     <product_price>143.33</product_price> 
     <product_property/> 
    </data> 
    <data> 
     <customer_name>Mustermann</customer_name> 
     <contract_id>C2</contract_id> 
     <product_name>Product C2.P2</product_name> 
     <product_price>231.76</product_price> 
     <product_property>Property C2.P2.A</product_property> 
    </data> 
    <data> 
     <customer_name>Mustermann</customer_name> 
     <contract_id>C2</contract_id> 
     <product_name>Product C2.P2</product_name> 
     <product_price>231.76</product_price> 
     <product_property>Property C2.P2.B</product_property> 
    </data> 
    <data> 
     <customer_name>Mustermann</customer_name> 
     <contract_id>C3</contract_id> 
     <product_name/> 
     <product_price/> 
     <product_property/> 
    </data> 
</output> 

我嘗試以下XSLT樣式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:template match="/customer"> 
    <output> 
    <xsl:for-each select="contract"> 
    <xsl:choose> 
    <xsl:when test="products/product"> 
     <xsl:for-each select="products/product"> 
     <xsl:choose> 
      <xsl:when test="properties/property"> 
      <xsl:for-each select="properties/property"> 
       <xsl:call-template name="data"> 
       <xsl:with-param name="customer_name" select="/customer/name"/> 
       <xsl:with-param name="contract_id" select="../../../../contract_id"/> 
       <xsl:with-param name="product_name" select="../../name"/> 
       <xsl:with-param name="product_price" select="../../price"/> 
       <xsl:with-param name="property" select="."/> 
       </xsl:call-template> 
      </xsl:for-each> 
      </xsl:when> 
      <xsl:otherwise> 
      <xsl:call-template name="data"> 
       <xsl:with-param name="customer_name" select="/customer/name"/> 
       <xsl:with-param name="contract_id" select="../../contract_id"/> 
       <xsl:with-param name="product_name" select="name"/> 
       <xsl:with-param name="product_price" select="price"/> 
      </xsl:call-template> 
      </xsl:otherwise>  
     </xsl:choose> 
     </xsl:for-each> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:call-template name="data"> 
      <xsl:with-param name="customer_name" select="/customer/name"/> 
      <xsl:with-param name="contract_id" select="contract_id"/> 
      </xsl:call-template> 
     </xsl:otherwise>  
    </xsl:choose> 
    </xsl:for-each> 
    </output> 
</xsl:template> 
<xsl:template name="data"> 
<xsl:param name="customer_name"/> 
<xsl:param name="contract_id"/> 
<xsl:param name="product_name"/> 
<xsl:param name="product_price"/> 
<xsl:param name="property"/> 
    <data> 
    <customer_name><xsl:value-of select="$customer_name"/></customer_name> 
    <contract_id><xsl:value-of select="$contract_id"/></contract_id> 
    <product_name><xsl:value-of select="$product_name"/></product_name> 
    <product_price><xsl:value-of select="$product_price"/></product_price> 
    <product_property><xsl:value-of select="$property"/></product_property> 
    </data> 
</xsl:template> 
</xsl:stylesheet> 

這將產生期望的結果,但對我來說,這看起來相當醜陋。 對於只執行嵌套循環(合同上的循環,循環內部產品等等)的樸素方法不起作用,因爲它沒有選取所有輸出節點(例如沒有產品的合同被忽略)。

背景:我想在oracle xmltable查詢中使用xslt樣式表,並將中間結果映射到簡單的行/列輸出。因此,每個結果都需要位於其自己的行中。

任何幫助表示讚賞!

回答

1

這產生了所需的結果,但對我而言,這看起來相當難看。

嗯,這是一個醜陋的問題。結果對於任何事情真的有用嗎?我會認爲每個級別的單獨表格(因此是單獨的樣式表)會更實用。

不過,如果這就是你想要的結果,您可以嘗試一個更優雅的方式來生產它:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="/customer"> 
    <output> 
     <xsl:apply-templates/> 
    </output> 
</xsl:template> 

<xsl:template match="contract[not(products)] | product[not(properties)] | property"> 
    <data> 
     <customer_name><xsl:value-of select="ancestor::customer/name"/></customer_name> 
     <contract_id><xsl:value-of select="ancestor-or-self::contract/contract_id"/></contract_id> 
     <product_name><xsl:value-of select="ancestor-or-self::product/name"/></product_name> 
     <product_price><xsl:value-of select="ancestor-or-self::product/price"/></product_price> 
     <product_property><xsl:value-of select="self::property"/></product_property> 
    </data> 
</xsl:template> 

<xsl:template match="text()"/> 

</xsl:stylesheet> 
+0

謝謝您的回答!這樣更優雅!一些背景:我們在oracle數據庫中有xml-data,用戶可以根據自己指定的xpath-queries來定義報告。 XML數據的結構如我的問題所示,結果應該基於表格。所以是的,結果是有用的:) – 2015-02-12 08:14:32

+0

更多背景:報告可能是「獲取所有合同ID,如果存在,產品的ID」。用戶不知道xml-data中存在哪些節點,並且被限制爲指定xpath-expressions。 – 2015-02-12 08:22:54