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

时间:2021-07-02 20:24:16

I have an XML document that models this hierarchy of tasks:

我有一个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

Unknown number of Customers, Products, and Tasks per product. There can also be an unknown number of subtasks, so we could see this:

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

19.16.8.17.1 Subtask Something

The XML looks like this:

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>

My first attempt at an XSLT is this:

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

As you can see, I'm taking a very naive approach to parsing this thing. I would like to list all the tasks & subtasks of my particular WBS, no matter how many levels deep it goes. How do I do that?

正如您所看到的,我使用了一种非常天真的方法来解析这个东西。我想列出我的特定WBS的所有任务和子任务,不管它有多深。我该怎么做呢?

2 个解决方案

#1


2  

Looks like musikk beat me to an explanation, but here's a demo xslt that does what I understand you want. In general, avoid xsl:for-each for most of the things you think you should use for-each for (i.e. for which you would use for-each in other languages). Instead, use apply-templates or call-templates as musikk says. Read up on modes (mode="foo") too if you have to process the same content several times (e.g. to generate a table of contents and then the body and then an index).

听起来像musikk给了我一个解释,但这是一个演示xslt,它能做我理解的你想要的。一般来说,对于大多数您认为应该使用for-each的东西(例如,您将在其他语言中使用for-each),要避免使用xsl:for-each。相反,像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>

This produces:

这产生:

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

#2


3  

Instead of nesting xsl:for-each directly just declare a few templates with xsl:template and call them (recursively) via

与直接嵌套xsl:for-each不同,只需使用xsl:template声明几个模板,并通过(递归地)调用它们

  1. xsl:call-template if there is a specific named template you want or
  2. 如果有您想要的特定命名模板,则调用模板
  3. xsl:apply-templates if a template should be used based on an XPath expression (which in your case might be as simple as "task"
  4. 如果模板应该基于XPath表达式(在您的例子中可能像“任务”一样简单),那么应用程序模板

For basic usage xsl:apply-templates should be enough but sometimes you want to match a specific rule.

对于基本用法xsl:apply模板应该足够了,但是有时候您需要匹配特定的规则。

I would do it like this (note this is quite rough... I think you can figure out the details with a little help from e.g. w3schools):

我会这样做(注意这很粗略……)我想你可以从w3schools那里得到一点帮助来弄清楚细节。

  1. one template for tasks and one for task
  2. 一个任务模板,一个任务模板
  3. the template for tasks walks over the task nodes and calls the task template for each task node
  4. 任务模板遍历任务节点并为每个任务节点调用任务模板
  5. the task template itself calls the tasks template again if there is a tasks node
  6. 如果有任务节点,任务模板本身将再次调用任务模板

#1


2  

Looks like musikk beat me to an explanation, but here's a demo xslt that does what I understand you want. In general, avoid xsl:for-each for most of the things you think you should use for-each for (i.e. for which you would use for-each in other languages). Instead, use apply-templates or call-templates as musikk says. Read up on modes (mode="foo") too if you have to process the same content several times (e.g. to generate a table of contents and then the body and then an index).

听起来像musikk给了我一个解释,但这是一个演示xslt,它能做我理解的你想要的。一般来说,对于大多数您认为应该使用for-each的东西(例如,您将在其他语言中使用for-each),要避免使用xsl:for-each。相反,像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>

This produces:

这产生:

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

#2


3  

Instead of nesting xsl:for-each directly just declare a few templates with xsl:template and call them (recursively) via

与直接嵌套xsl:for-each不同,只需使用xsl:template声明几个模板,并通过(递归地)调用它们

  1. xsl:call-template if there is a specific named template you want or
  2. 如果有您想要的特定命名模板,则调用模板
  3. xsl:apply-templates if a template should be used based on an XPath expression (which in your case might be as simple as "task"
  4. 如果模板应该基于XPath表达式(在您的例子中可能像“任务”一样简单),那么应用程序模板

For basic usage xsl:apply-templates should be enough but sometimes you want to match a specific rule.

对于基本用法xsl:apply模板应该足够了,但是有时候您需要匹配特定的规则。

I would do it like this (note this is quite rough... I think you can figure out the details with a little help from e.g. w3schools):

我会这样做(注意这很粗略……)我想你可以从w3schools那里得到一点帮助来弄清楚细节。

  1. one template for tasks and one for task
  2. 一个任务模板,一个任务模板
  3. the template for tasks walks over the task nodes and calls the task template for each task node
  4. 任务模板遍历任务节点并为每个任务节点调用任务模板
  5. the task template itself calls the tasks template again if there is a tasks node
  6. 如果有任务节点,任务模板本身将再次调用任务模板