如何使用XSLT递归将任何xml数据转换为html表视图:

时间:2010-11-16 06:51:22

标签: java xml xslt

对于给定的xml,我需要生成一个html表来表示xml中的值。 我需要任何keyN的递归,如果valueN是文本然后只是打印它。如果valueN是xml,则使用它的值打印(嵌套)表。我认为我对如何正确使用XSLT递归缺乏了解是问题的基础。任何帮助赞赏。

输入:

<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>

5 个答案:

答案 0 :(得分: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和具有名称和第一个子应用程序的表格单元格,然后将模板应用于下一个兄弟;根元素规则,启动细粒度遍历。

答案 1 :(得分: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. 完全推送式处理

答案 2 :(得分: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>

顺便说一下,重写xsl会更好:选择两个独立的模板。

答案 3 :(得分:0)

这听起来像家庭作业: - )

你在这里做了两件事:

  1. 您需要将XSLT模板应用于
  2. 的任意命名元素
  3. 确定元素的内容类型(文本或XML片段)。
  4. 您希望使用可以任意匹配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属性可消除歧义。

答案 4 :(得分: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>