如何获取XML文档的所有子节点?

时间:2010-07-02 14:21:22

标签: xml xslt

我有一个XML文档来模拟这个任务层次结构:

1     Customer
1.1   Product A
1.1.1 Task Alpha
1.1.2 Task Beta
1.2   Product B
1.2.1 Task Alpha
1.2.2 Task Gamma
2     Customer
2.1   Product W
2.1.1 Task Delta

每个产品的客户,产品和任务数量不明。也可能有未知数量的子任务,所以我们可以看到:

19.16.8.17.1 Subtask Something

XML看起来像这样:

<ROWSET>
    <ROW>
        <PROJECT_CODE>Don't Care</PROJECT_CODE>
    </ROW>
    <ROW>
        <PROJECT_CODE>WBS</PROJECT_CODE>
        <TASK_DETAIL>
            <TASKS>
                <TASK>
                    <TASK_CODE>1</TASK_CODE>
                    <TASK_DESCRIPTION>Customer 1</TASK_DESCRIPTION>
                    <TASKS>
                        <TASK>
                            <TASK_CODE>1.1</TASK_CODE>
                            <TASK_DESCRIPTION>Product A</TASK_DESCRIPTION>
                            <TASKS>
                                <TASK_CODE>1.1.1</TASK_CODE>
                                <TASK_DESCRIPTION>Task Alpha</TASK_DESCRIPTION>
                                <TASKS />
                                <TASK_CODE>1.1.2</TASK_CODE>
                                <TASK_DESCRIPTION>Task Beta</TASK_DESCRIPTION>
                                <TASKS />
                            </TASKS>
                        </TASK>
                        <TASK>
                            <TASK_CODE>1.2</TASK_CODE>
                            <TASK_DESCRIPTION>Product B</TASK_DESCRIPTION>
                            <TASKS>
                                <TASK_CODE>1.2.1</TASK_CODE>
                                <TASK_DESCRIPTION>Task Alpha</TASK_DESCRIPTION>
                                <TASKS />
                                <TASK_CODE>1.2.2</TASK_CODE>
                                <TASK_DESCRIPTION>Task Gamma</TASK_DESCRIPTION>
                                <TASKS />
                            </TASKS>
                        </TASK>
                    </TASKS>
                </TASK>
                <TASK>
                    <TASK_CODE>2</TASK_CODE>
                    <TASK_DESCRIPTION>Customer 2</TASK_DESCRIPTION>
                    <TASKS>
                        <TASK>
                            <TASK_CODE>2.1</TASK_CODE>
                            <TASK_DESCRIPTION>Product W</TASK_DESCRIPTION>
                            <TASKS>
                                <TASK_CODE>2.1.1</TASK_CODE>
                                <TASK_DESCRIPTION>Task Delta</TASK_DESCRIPTION>
                                <TASKS />
                            </TASKS>
                        </TASK>
                    </TASKS>
                </TASK>
            </TASKS>
        </TASK_DETAIL>
    </ROW>
</ROWSET>

我第一次尝试使用XSLT是这样的:

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="ROW">
    <xsl:apply-templates select="PROJECT_CODE[.='WBS']"/>
</xsl:template>
<xsl:template match="PROJECT_CODE">
    <h1><xsl:value-of select="."/></h1>
    <table>
        <tr>
            <th>Task Code</th>
            <th>Description</th>
        </tr>
        <xsl:for-each select="./../TASK_DETAIL/TASKS/TASK">
        <tr>
            <td><xsl:value-of select="TASK_CODE"/></td>
            <td><xsl:value-of select="TASK_DESCRIPTION"/></td>
        </tr>
            <xsl:for-each select="./TASKS/TASK">
                <tr>
                    <td><xsl:value-of select="TASK_CODE"/></td>
                    <td><xsl:value-of select="TASK_DESCRIPTION"/></td>
                </tr>
            </xsl:for-each>
        </xsl:for-each>
    </table>
</xsl:template>

正如您所看到的,我正在采用一种非常天真的方法来解析这个问题。我想列出所有的任务和安排;我的特定WBS的子任务,无论它有多少级别。我该怎么做?

2 个答案:

答案 0 :(得分:3)

不是直接嵌套xsl:for-each,而是使用xsl:template声明一些模板并通过

调用它们(递归)
  1. xsl:call-template如果您想要特定的命名模板或
  2. xsl:apply-templates如果应该根据XPath表达式使用模板(在您的情况下可能就像“任务”一样简单
  3. 对于基本用法xsl:apply-templates应该足够了,但有时您希望匹配特定规则。

    我会这样做(注意这是非常粗略的......我想你可以通过例如w3schools的帮助来弄清楚细节):

    1. tasks的一个模板和task
    2. 的一个模板
    3. tasks的模板遍历task个节点,并为每个task节点调用task模板
    4. 如果有task节点,则tasks模板本身会再次调用tasks模板

答案 1 :(得分:2)

看起来musikk打败了我一个解释,但这是一个演示xslt,做我理解你想要的。一般情况下,请避免xsl:for-each用于您认为应该使用的大部分内容(即每个用于其他语言的内容)。相反,使用apply-templates或call-templates,如musikk所说。如果你必须多次处理相同的内容(例如,生成目录,然后是主体,然后是索引),也要读取模式(mode =“foo”)。

    <?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <html>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="ROW[./PROJECT_CODE='WBS']">
    <h1><xsl:value-of select="PROJECT_CODE"/></h1>
    <table>
      <tr>
        <th>Task Code</th>
        <th>Description</th>
      </tr>
      <xsl:apply-templates/>
    </table>
  </xsl:template>
  <xsl:template match="TASK">
    <tr>
      <td><xsl:value-of select="TASK_CODE"/></td>
      <td><xsl:value-of select="TASK_DESCRIPTION"/></td>
    </tr>
    <xsl:apply-templates/>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>

这会产生:

    <html>
   <body>
      <h1>WBS</h1>
      <table>
         <tr>
            <th>Task Code</th>
            <th>Description</th>
         </tr>
         <tr>
            <td>1</td>
            <td>Customer 1</td>
         </tr>
         <tr>
            <td>1.1</td>
            <td>Product A</td>
         </tr>
         <tr>
            <td>1.2</td>
            <td>Product B</td>
         </tr>
         <tr>
            <td>2</td>
            <td>Customer 2</td>
         </tr>
         <tr>
            <td>2.1</td>
            <td>Product W</td>
         </tr>
      </table>
   </body>
</html>