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声明几个模板,并通过(递归地)调用它们
-
xsl:call-template
if there is a specific named template you want or - 如果有您想要的特定命名模板,则调用模板
-
xsl:apply-templates
if a template should be used based on an XPath expression (which in your case might be as simple as "task" - 如果模板应该基于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那里得到一点帮助来弄清楚细节。
- one template for
tasks
and one fortask
- 一个任务模板,一个任务模板
- the template for
tasks
walks over thetask
nodes and calls thetask
template for eachtask
node - 任务模板遍历任务节点并为每个任务节点调用任务模板
- the
task
template itself calls thetasks
template again if there is atasks
node - 如果有任务节点,任务模板本身将再次调用任务模板
#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声明几个模板,并通过(递归地)调用它们
-
xsl:call-template
if there is a specific named template you want or - 如果有您想要的特定命名模板,则调用模板
-
xsl:apply-templates
if a template should be used based on an XPath expression (which in your case might be as simple as "task" - 如果模板应该基于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那里得到一点帮助来弄清楚细节。
- one template for
tasks
and one fortask
- 一个任务模板,一个任务模板
- the template for
tasks
walks over thetask
nodes and calls thetask
template for eachtask
node - 任务模板遍历任务节点并为每个任务节点调用任务模板
- the
task
template itself calls thetasks
template again if there is atasks
node - 如果有任务节点,任务模板本身将再次调用任务模板