2010-11-16 62 views
2

對於給定的xml,我需要生成一個html表來表示xml中的值。 我需要任何keyN的遞歸,如果valueN是文本然後只是打印它。如果valueN是xml,那麼用它的值打印一個(嵌套的)表格。我認爲我對如何正確使用XSLT遞歸的理解不足是問題的基礎。任何幫助讚賞。如何使用XSLT遞歸將任何xml數據轉換爲html表視圖:

輸入:

<root> 
    <key1> Text Value </key1> 
<key2> 
    <a> aaa </a> 
    <b> bbb </b> 
</key2> 
<keyN> valueN </keyN> 
<root> 

輸出:

<table border="1px"> 
    <tr> 
     <td> key1 </td> 
     <td> Text Value </td> 
    </tr> 

    <tr> 
     <td> key2 </td> 
     <td> 
      <table border="1px"> 
       <tr> <td> a </td> <td> aaa </td> </tr> 
       <tr> <td> b </td> <td> bbb </td> </tr> 
      </table> 
     </td> 
    </tr> 

    <tr> 
     <td> keyN </td> 
     <td> 
      valueN (if valueN is text) 
        OR 
      <table> ... </table> (if valueN is xml) 
     <td> 
    </tr> 
</table> 
+0

問得好,+1。查看我的解決方案,獲得完全符合XSLT精神的短而強大的推式XSLT解決方案。 :) – 2010-11-16 14:39:13

回答

5

這個樣式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="/*//*[1]"> 
     <table border="1"> 
      <xsl:call-template name="makeRow"/> 
     </table> 
    </xsl:template> 
    <xsl:template match="*" name="makeRow"> 
     <tr> 
      <td> 
       <xsl:value-of select="name()"/> 
      </td> 
      <td> 
       <xsl:apply-templates select="node()[1]"/> 
      </td> 
     </tr> 
     <xsl:apply-templates select="following-sibling::node()[1]"/> 
    </xsl:template> 
    <xsl:template match="/*"> 
     <xsl:apply-templates select="node()[1]"/> 
    </xsl:template> 
</xsl:stylesheet> 

輸出:

<table border="1"> 
    <tr> 
     <td>key1</td> 
     <td> Text Value </td> 
    </tr> 
    <tr> 
     <td>key2</td> 
     <td> 
      <table border="1"> 
       <tr> 
        <td>a</td> 
        <td> aaa </td> 
       </tr> 
       <tr> 
        <td>b</td> 
        <td> bbb </td> 
       </tr> 
      </table></td> 
    </tr> 
    <tr> 
     <td>keyN</td> 
     <td> valueN </td> 
    </tr> 
</table> 

注意:此使用細粒度遍歷圖案。三條規則:「根元素的第一個孩子的後代」,輸出table,並致電makeRow; makeRow(匹配任何不是第一個子代後代或根元素的元素)輸出tr以及具有名稱和第一個子應用的表格單元,然後將模板應用於下一個兄弟;根元素規則,開始細粒度遍歷。

+0

+1爲一個很好的解決方案。 – 2010-11-16 14:21:19

+0

@Alejandro,非常好的解決方案。 – 2010-11-16 15:13:59

+0

+1這太棒了!我希望我知道像你這樣的xslt :) – sanjayav 2010-11-26 06:16:15

0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="/"> 
     <html> 
      <body> 
       <!-- apply root as a table --> 
       <xsl:apply-templates select="root" mode="table"/> 
      </body> 
     </html> 
    </xsl:template> 

    <xsl:template match="node()[not(self::text())]" mode="table"> 
     <table border="1px"> 
      <!-- create table and process children as rows --> 
      <xsl:apply-templates select="node()" mode="row"/> 
     </table> 
    </xsl:template> 

    <xsl:template match="node()[not(self::text())]" mode="row"> 
     <tr> 
      <!-- make decision here. 
       If row contains only text - apply is as a simple row 
       In case there are some other nodes proces them as a table. 
      --> 
      <xsl:choose> 
       <xsl:when test=". = text()"> 
        <xsl:apply-templates select="." mode="column"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <td><xsl:value-of select="name()"/></td> 
        <td><xsl:apply-templates select="." mode="table"/></td> 
       </xsl:otherwise> 
      </xsl:choose> 
     </tr> 
    </xsl:template> 

    <xsl:template match="node()" mode="column"> 
     <td><xsl:value-of select="name()"/></td> 
     <td><xsl:value-of select="."/></td> 
    </xsl:template> 
</xsl:stylesheet> 

輸出:

<html> 
    <body> 
     <table border="1px"> 
      <tr> 
       <td>key1</td> 
       <td>Text Value</td> 
      </tr> 

      <tr> 
       <td>key2</td> 
       <td> 
       <table border="1px"> 
        <tr> 
         <td>a</td> 
         <td>aaa</td> 
        </tr> 

        <tr> 
         <td>b</td> 
         <td>bbb</td> 
        </tr> 
       </table> 
       </td> 
      </tr> 

      <tr> 
       <td>keyN</td> 
       <td>valueN</td> 
      </tr> 

     </table> 
    </body> 
</html> 

BTW會更好重寫的xsl:選擇作爲兩個獨立的模板。

+0

感謝您的解決方案。 – sanjayav 2010-11-26 06:17:24

0

這聽起來像功課:-)

你有兩件事情在這裏工作:

  1. ,你需要使用XSLT模板來任意命名元素,
  2. 確定是什麼類型元素具有的內容(文本或XML片段)。

您想要使用可以與XML中的元素任意匹配的模板(即不是具有特定名稱的元素)。 <xsl:template match="*">將匹配您的XML文檔中的所有元素。

當你遇到一個元素,你需要創建一個表:

<xsl:template match="*"> 
    <table border="1px"> 
    </table> 
</xsl:template> 

現在,我們想看看是否我們正在處理一個XML片段(元素)或一段文字。要做到這一點,我們匹配node()。請記住,節點可以是元素,文本,空白,處理指令或XML文檔中的註釋。當你匹配一個節點,你想創建一個新的錶行,並顯示當前節點的名稱:

<xsl:template match="node()"> 
    <tr> 
    <td> 
     <xsl:value-of select="local-name()"/> 
    </td> 
    </tr> 
</xsl:template> 

然後你需要制定出節點是否是一個文本節點或沒有。您可以使用<xsl:if><xsl:choose>。我傾向於選擇後者。如果它是文本節點,則顯示文本的值,否則像XML片段一樣處理節點並再次調用我們的初始模板(這是遞歸部分)。

... 
<xsl:choose> 
    <xsl:when test="current() = text()"> 
    <td> 
     <xsl:value-of select="." /> 
    </td> 
    </xsl:when> 
    <xsl:otherwise> 
    <td> 
     <xsl:apply-templates select="*" mode="table"/> 
    </td> 
    </xsl:otherwise> 
</xsl:choose> 
... 

下面是最終的解決方案。

<xsl:template match="/root"> 
    <xsl:apply-templates select="*" mode="table" /> 
</xsl:template> 

<xsl:template match="*" mode="table"> 
    <table border="1px"> 
    <xsl:apply-templates select="." mode="table-row" /> 
    </table> 
</xsl:template> 

<xsl:template match="node()" mode="table-row"> 
    <tr> 
    <td> 
     <xsl:value-of select="local-name()"/> 
    </td> 
    <xsl:choose> 
     <xsl:when test="current() = text()"> 
     <td> 
      <xsl:value-of select="." /> 
     </td> 
     </xsl:when> 
     <xsl:otherwise> 
     <td> 
      <xsl:apply-templates select="*" mode="table"/> 
     </td> 
     </xsl:otherwise> 
    </xsl:choose> 
    </tr> 
</xsl:template> 

我使用模板上的mode屬性,因爲一個元素也是一個XML文檔中的節點和<xsl:apply-templates select="*"/>將匹配都<xsl:template match="*">以及<xsl:template match="node()">。使用mode屬性可消除不明確性。

+0

以及它的工作,而不是在家工作:)。謝謝你的答案。 – sanjayav 2010-11-26 06:17:01

3

這種轉變

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

<xsl:template match="/*"> 
    <table border="1px"> 
    <xsl:apply-templates/> 
    </table> 
</xsl:template> 

<xsl:template match="*[*][parent::*]"> 
    <tr> 
     <td><xsl:value-of select="name()"/></td> 
     <td> 
     <table border="1px"> 
      <xsl:apply-templates/> 
     </table> 
     </td> 
    </tr> 
</xsl:template> 

<xsl:template match="*[not(*)]"> 
    <tr> 
     <td><xsl:value-of select="name()"/></td> 
    <td><xsl:value-of select="."/></td> 
    </tr> 
</xsl:template> 
</xsl:stylesheet> 

時所提供的XML文檔應用:

<root> 
    <key1> Text Value </key1> 
    <key2> 
     <a> aaa </a> 
     <b> bbb </b> 
    </key2> 
    <keyN> valueN </keyN> 
</root> 

產生想要的,正確的結果

<table border="1px"> 
    <tr> 
     <td>key1</td> 
     <td> Text Value </td> 
    </tr> 
    <tr> 
     <td>key2</td> 
     <td> 
     <table border="1px"> 
      <tr> 
       <td>a</td> 
       <td> aaa </td> 
      </tr> 
      <tr> 
       <td>b</td> 
       <td> bbb </td> 
      </tr> 
     </table> 
     </td> 
    </tr> 
    <tr> 
     <td>keyN</td> 
     <td> valueN </td> 
    </tr> 
</table> 

待辦事項XSLT的力量:

  1. 沒有明確的遞歸

  2. 沒有任何模板內的條件

  3. 完全推式加工

+1

'​​key2'後面的表應該包裹在'​​'本身。很好的解決方案。 – 2010-11-16 15:15:54

+0

@Alex Nikolaenkov:謝謝你注意到這一點。現在修復。 – 2010-11-16 15:35:22

+0

我喜歡推式。做得好! – 2013-06-23 20:53:26

0

附加功能用於顯示屬性

<xsl:stylesheet version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
     <xsl:template match="/*//*[1]"> 
      <table border="1"> 
       <xsl:call-template name="makeRow" /> 
      </table> 
     </xsl:template> 
     <xsl:template match="*" name="makeRow"> 
      <tr> 
       <td><xsl:value-of select="name()" /> <xsl:if 
        test="count(@*) > 0"> 
        <table> 
         <xsl:apply-templates select="@*" /> 
        </table> 
       </xsl:if></td> 
       <td><xsl:apply-templates select="node()[1]" /></td> 
      </tr> 
      <xsl:apply-templates select="following-sibling::node()[1]" /> 
     </xsl:template> 
     <xsl:template match="/*"> 
      <xsl:apply-templates select="node()[1]" /> 
     </xsl:template> 
     <xsl:template match="@*"> 
      <tr> 
       <td>@<xsl:value-of select="name()" /></td> 
       <td><xsl:value-of select="." /></td> 
      </tr> 
     </xsl:template> 
    </xsl:stylesheet> 
+0

請發帖解釋一下答案。 – 2012-10-26 06:28:47