使用 XSLT 从未知 XML 创建格式良好的嵌套 table
Using XSLT to create a well formatted nested table from unknown XML
给我的任务:
我的老板要我创建一个采用 XML 未知结构的 XSLT,并将其放入嵌套的 table(标记名称为 table headers)而不重复 table headers 尽可能。我几乎可以如愿以偿(table)
问题:
我现在只使用 XSLT 几天了(已经阅读了一些教程并稍微玩了一下),所以如果有人能给我指出一个好的方向,告诉我在哪里可以找到有助于我解决问题的信息,它将不胜感激。
环境数据: 我正在使用 XSLT 和 PHP (DOM objects)。我的老板希望我成为公司的 XSLT 专家,所以如果可以通过纯 XSLT 实现,我将不胜感激。
附加信息:应回复者的要求,附加信息(代码)是below.The任务是将类似XML片段的内容下面进入table下方进行展示。不幸的是,我的代码目前处于不断变化的状态,所以我不会 posting 它(如果问题在我再次得到它 stable 时仍然悬而未决,我会 post它)。
XML 片段:
<root>
<request>
<details>
<columnname>name1</columnname>
<operator></operator>
<value>val</value>
<seq>1</seq>
</details>
</request>
<request>
<details>
<columnname>name2</columnname>
<operator>OP</operator>
<value>val</value>
<seq>2</seq>
</details>
</request>
<request>
<details>
<columnname>name3</columnname>
<value>val</value>
<seq>3</seq>
</details>
</request>
<response>
<details>
<columnname>name4</columnname>
<value>val</value>
<seq>4</seq>
</details>
</response>
</root>
期望输出
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>root</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>request</th>
<th>response</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>details</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>columnname</th>
<th>operator</th>
<th>value</th>
<th>seq</th>
</tr>
<tr>
<td>name1</td>
<td></td>
<td>val</td>
<td>1</td>
</tr>
<tr>
<td>name2</td>
<td>OP</td>
<td>val</td>
<td>2</td>
</tr>
<tr>
<td>name3</td>
<td></td>
<td>val</td>
<td>3</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>details</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>columnname</th>
<th>value</th>
<th>seq</th>
</tr>
<tr>
<td>name4</td>
<td>val</td>
<td>4</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
对 michael.hor257k 的回复: 它看起来像这样。
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>root</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>parent</th>
<th>uncle</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>child</th>
<th>nephew</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>string</th>
<th>number</th>
<th>grandchild</th>
<th>date</th>
</tr>
<tr>
<td>A</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>B</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>C</td>
<td>1</td>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>string</th>
<th>substring</th>
<th>number</th>
</tr>
<tr>
<td>DD</td>
<td>EE</td>
<td>33</td>
</tr>
</table>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>2</td>
<td></td>
<td></td>
</tr>
<tr>
<td>F</td>
<td></td>
<td></td>
<td>2015-02-12</td>
</tr>
</table>
</td>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>string</th>
</tr>
<tr>
<td>G</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>niece</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>string</th>
</tr>
<tr>
<td>H</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
正如我在评论中所说,这不会很简单。基本上,您想要删除任何具有相同 path 的重复节点(其中仅使用节点名称计算路径)并显示生成的层次结构。
这需要进行预处理,为所有节点分配路径。我们还需要为每个节点提供其父路径 - 以便稍后可以由其新父节点调用(以前可能是其叔叔或曾叔叔或...)。
在第二步也是最后一步中,我们将 Muenchian grouping 应用到第一遍的结果,只留下不同的路径节点。
在这个例子中,我将只处理元素,结果将以无序列表的形式呈现。
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="node-by-path" match="node" use="@path" />
<xsl:key name="node-by-parent-path" match="node" use="@parent-path" />
<xsl:template match="/">
<!-- first-pass -->
<xsl:variable name="first-pass">
<xsl:apply-templates select="*" mode="firstpass"/>
</xsl:variable>
<!-- output -->
<ul>
<xsl:apply-templates select="exsl:node-set($first-pass)/node[@parent-path='']" />
</ul>
</xsl:template>
<xsl:template match="*" mode="firstpass">
<xsl:variable name="parent-path">
<xsl:for-each select="ancestor::*">
<xsl:value-of select="concat('/', name())"/>
</xsl:for-each>
</xsl:variable>
<node name="{name()}" parent-path="{$parent-path}" path="{concat($parent-path, '/', name())}">
<xsl:apply-templates select="*" mode="firstpass"/>
</node>
</xsl:template>
<xsl:template match="node">
<li>
<xsl:value-of select="@name"/>
<xsl:variable name="next" select="key('node-by-parent-path', @path)" />
<xsl:if test="$next">
<ul>
<xsl:apply-templates select="$next[count(. | key('node-by-path', @path)[1]) = 1]"/>
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
测试输入XML
<root>
<parent>
<child>
<string>A</string>
<string>B</string>
</child>
<child>
<string>C</string>
<number>1</number>
<number>2</number>
<grandchild>
<string>DD</string>
<substring>EE</substring>
<number>33</number>
</grandchild>
</child>
</parent>
<parent>
<child>
<string>F</string>
<date>2015-02-12</date>
</child>
<nephew>
<string>G</string>
</nephew>
</parent>
<uncle>
<niece>
<string>H</string>
</niece>
</uncle>
</root>
结果
<?xml version="1.0" encoding="UTF-8"?>
<ul>
<li>root<ul>
<li>parent<ul>
<li>child<ul>
<li>string</li>
<li>number</li>
<li>grandchild<ul>
<li>string</li>
<li>substring</li>
<li>number</li>
</ul>
</li>
<li>date</li>
</ul>
</li>
<li>nephew<ul>
<li>string</li>
</ul>
</li>
</ul>
</li>
<li>uncle<ul>
<li>niece<ul>
<li>string</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
渲染
给我的任务: 我的老板要我创建一个采用 XML 未知结构的 XSLT,并将其放入嵌套的 table(标记名称为 table headers)而不重复 table headers 尽可能。我几乎可以如愿以偿(table)
问题: 我现在只使用 XSLT 几天了(已经阅读了一些教程并稍微玩了一下),所以如果有人能给我指出一个好的方向,告诉我在哪里可以找到有助于我解决问题的信息,它将不胜感激。
环境数据: 我正在使用 XSLT 和 PHP (DOM objects)。我的老板希望我成为公司的 XSLT 专家,所以如果可以通过纯 XSLT 实现,我将不胜感激。
附加信息:应回复者的要求,附加信息(代码)是below.The任务是将类似XML片段的内容下面进入table下方进行展示。不幸的是,我的代码目前处于不断变化的状态,所以我不会 posting 它(如果问题在我再次得到它 stable 时仍然悬而未决,我会 post它)。
XML 片段:
<root>
<request>
<details>
<columnname>name1</columnname>
<operator></operator>
<value>val</value>
<seq>1</seq>
</details>
</request>
<request>
<details>
<columnname>name2</columnname>
<operator>OP</operator>
<value>val</value>
<seq>2</seq>
</details>
</request>
<request>
<details>
<columnname>name3</columnname>
<value>val</value>
<seq>3</seq>
</details>
</request>
<response>
<details>
<columnname>name4</columnname>
<value>val</value>
<seq>4</seq>
</details>
</response>
</root>
期望输出
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>root</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>request</th>
<th>response</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>details</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>columnname</th>
<th>operator</th>
<th>value</th>
<th>seq</th>
</tr>
<tr>
<td>name1</td>
<td></td>
<td>val</td>
<td>1</td>
</tr>
<tr>
<td>name2</td>
<td>OP</td>
<td>val</td>
<td>2</td>
</tr>
<tr>
<td>name3</td>
<td></td>
<td>val</td>
<td>3</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>details</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>columnname</th>
<th>value</th>
<th>seq</th>
</tr>
<tr>
<td>name4</td>
<td>val</td>
<td>4</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
对 michael.hor257k 的回复: 它看起来像这样。
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>root</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>parent</th>
<th>uncle</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>child</th>
<th>nephew</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>string</th>
<th>number</th>
<th>grandchild</th>
<th>date</th>
</tr>
<tr>
<td>A</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>B</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>C</td>
<td>1</td>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>string</th>
<th>substring</th>
<th>number</th>
</tr>
<tr>
<td>DD</td>
<td>EE</td>
<td>33</td>
</tr>
</table>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>2</td>
<td></td>
<td></td>
</tr>
<tr>
<td>F</td>
<td></td>
<td></td>
<td>2015-02-12</td>
</tr>
</table>
</td>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>string</th>
</tr>
<tr>
<td>G</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>niece</th>
</tr>
<tr>
<td>
<table border="1" style="border-collapse:collapse;width:100%">
<tr>
<th>string</th>
</tr>
<tr>
<td>H</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
正如我在评论中所说,这不会很简单。基本上,您想要删除任何具有相同 path 的重复节点(其中仅使用节点名称计算路径)并显示生成的层次结构。
这需要进行预处理,为所有节点分配路径。我们还需要为每个节点提供其父路径 - 以便稍后可以由其新父节点调用(以前可能是其叔叔或曾叔叔或...)。
在第二步也是最后一步中,我们将 Muenchian grouping 应用到第一遍的结果,只留下不同的路径节点。
在这个例子中,我将只处理元素,结果将以无序列表的形式呈现。
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="node-by-path" match="node" use="@path" />
<xsl:key name="node-by-parent-path" match="node" use="@parent-path" />
<xsl:template match="/">
<!-- first-pass -->
<xsl:variable name="first-pass">
<xsl:apply-templates select="*" mode="firstpass"/>
</xsl:variable>
<!-- output -->
<ul>
<xsl:apply-templates select="exsl:node-set($first-pass)/node[@parent-path='']" />
</ul>
</xsl:template>
<xsl:template match="*" mode="firstpass">
<xsl:variable name="parent-path">
<xsl:for-each select="ancestor::*">
<xsl:value-of select="concat('/', name())"/>
</xsl:for-each>
</xsl:variable>
<node name="{name()}" parent-path="{$parent-path}" path="{concat($parent-path, '/', name())}">
<xsl:apply-templates select="*" mode="firstpass"/>
</node>
</xsl:template>
<xsl:template match="node">
<li>
<xsl:value-of select="@name"/>
<xsl:variable name="next" select="key('node-by-parent-path', @path)" />
<xsl:if test="$next">
<ul>
<xsl:apply-templates select="$next[count(. | key('node-by-path', @path)[1]) = 1]"/>
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
测试输入XML
<root>
<parent>
<child>
<string>A</string>
<string>B</string>
</child>
<child>
<string>C</string>
<number>1</number>
<number>2</number>
<grandchild>
<string>DD</string>
<substring>EE</substring>
<number>33</number>
</grandchild>
</child>
</parent>
<parent>
<child>
<string>F</string>
<date>2015-02-12</date>
</child>
<nephew>
<string>G</string>
</nephew>
</parent>
<uncle>
<niece>
<string>H</string>
</niece>
</uncle>
</root>
结果
<?xml version="1.0" encoding="UTF-8"?>
<ul>
<li>root<ul>
<li>parent<ul>
<li>child<ul>
<li>string</li>
<li>number</li>
<li>grandchild<ul>
<li>string</li>
<li>substring</li>
<li>number</li>
</ul>
</li>
<li>date</li>
</ul>
</li>
<li>nephew<ul>
<li>string</li>
</ul>
</li>
</ul>
</li>
<li>uncle<ul>
<li>niece<ul>
<li>string</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
渲染