分组并填充 xslt 中缺失的 table 元素
Grouping and filling missing table elements in xslt
我有一个 xml,如下所示。我正在尝试将此 xml 转换为 html table。 "ServerName" 节点成为列 header。行分组在 "Category" 名称下。当任何 "Data" 节点不存在 "Category->Name" 或 "Parameter->key" 时,它应该显示 "NA".
我尽力自己解决了这个问题,但是我在分组和填充缺失元素方面失败得很惨。非常感谢任何帮助。
<Results>
<Data>
<ServerName>Server1</ServerName>
<CategorizedData>
<Category>
<Parameters>
<Parameter key="A" value="1" />
<Parameter key="B" value="2" />
<Parameter key="C" value="3" />
</Parameters>
</Category>
<Category Name="Misc">
<Parameters>
<Parameter key="A" value="2" />
<Parameter key="B" value="5" />
</Parameters>
</Category>
</CategorizedData>
</Data>
<Data>
<ServerName>Server2</ServerName>
<CategorizedData>
<Category>
<Parameters>
<Parameter key="A" value="10" />
<Parameter key="B" value="20" />
<Parameter key="D" value="30" />
</Parameters>
</Category>
<Category Name="Misc">
<Parameters>
<Parameter key="C" value="2" />
<Parameter key="D" value="5" />
</Parameters>
</Category>
</CategorizedData>
</Data>
</Results>
<table>
<tr>
<td></td>
<td>Server1</td>
<td>Server2</td>
</tr>
<tr>
<td>A</td>
<td>1</td>
<td>10</td>
</tr>
<tr>
<td>B</td>
<td>2</td>
<td>20</td>
</tr>
<tr>
<td>C</td>
<td>3</td>
<td>NA</td>
</tr>
<tr>
<td>D</td>
<td>NA</td>
<td>30</td>
</tr>
<tr>
<td>Misc</td>
<td></td>
<td></td>
</tr>
<tr>
<td>A</td>
<td>2</td>
<td>NA</td>
</tr>
<tr>
<td>B</td>
<td>5</td>
<td>NA</td>
</tr>
<tr>
<td>C</td>
<td>NA</td>
<td>2</td>
</tr>
<tr>
<td>D</td>
<td>NA</td>
<td>5</td>
</tr>
</table>
使用 XSLT 2 或 3,您可以使用嵌套 for-each-group
:
来解决它
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Results">
<table>
<xsl:variable name="servers" select="Data/ServerName"/>
<thead>
<tr>
<th>Key</th>
<xsl:for-each select="$servers">
<th>{.}</th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:for-each-group select=".//Category" group-by="string(@Name)">
<xsl:variable name="cat-group" select="current-group()"/>
<tr>
<th>{current-grouping-key()}</th>
<th>{$servers[1]}</th>
<th>{$servers[2]}</th>
</tr>
<xsl:for-each-group select="current-group()" group-by="Parameters/Parameter/@key">
<tr>
<td>{current-grouping-key()}</td>
<td>{($cat-group[ancestor::Data[ServerName = $servers[1]]]/Parameters/Parameter[@key = current-grouping-key()]/@value, 'N/A')[1]}</td>
<td>{($cat-group[ancestor::Data[ServerName = $servers[2]]]/Parameters/Parameter[@key = current-grouping-key()]/@value, 'N/A')[1]}</td>
</tr>
</xsl:for-each-group>
</xsl:for-each-group>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6qM2e2i/3 给出输出
<table>
<thead>
<tr>
<th>Key</th>
<th>Server1</th>
<th>Server2</th>
</tr>
</thead>
<tbody>
<tr>
<th></th>
<th>Server1</th>
<th>Server2</th>
</tr>
<tr>
<td>A</td>
<td>1</td>
<td>10</td>
</tr>
<tr>
<td>B</td>
<td>2</td>
<td>20</td>
</tr>
<tr>
<td>C</td>
<td>3</td>
<td>N/A</td>
</tr>
<tr>
<td>D</td>
<td>N/A</td>
<td>30</td>
</tr>
<tr>
<th>Misc</th>
<th>Server1</th>
<th>Server2</th>
</tr>
<tr>
<td>A</td>
<td>2</td>
<td>N/A</td>
</tr>
<tr>
<td>B</td>
<td>5</td>
<td>N/A</td>
</tr>
<tr>
<td>C</td>
<td>N/A</td>
<td>2</td>
</tr>
<tr>
<td>D</td>
<td>N/A</td>
<td>5</td>
</tr>
</tbody>
</table>
至于服务器列数的动态,希望适配
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Results">
<table>
<xsl:variable name="servers" select="Data/ServerName"/>
<thead>
<tr>
<th>Key</th>
<xsl:for-each select="$servers">
<th>{.}</th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:for-each-group select=".//Category" group-by="string(@Name)">
<xsl:variable name="cat-group" select="current-group()"/>
<tr>
<th>{current-grouping-key()}</th>
<xsl:for-each select="$servers">
<th>{.}</th>
</xsl:for-each>
</tr>
<xsl:for-each-group select="current-group()" group-by="Parameters/Parameter/@key">
<tr>
<td>{current-grouping-key()}</td>
<xsl:for-each select="$servers">
<td>{($cat-group[ancestor::Data[ServerName = current()]]/Parameters/Parameter[@key = current-grouping-key()]/@value, 'N/A')[1]}</td>
</xsl:for-each>
</tr>
</xsl:for-each-group>
</xsl:for-each-group>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
使用 XSLT 1:
例子:
https://xsltfiddle.liberty-development.net/bFukv8n
<?xml version="1.0" encoding="UTF-8"?>
<xsl:template match="/">
<xsl:variable name="vMsg" select="."/>
<xsl:variable name="vServerNames" select="/Results/Data/ServerName" />
<table>
<tr>
<td/>
<xsl:for-each select="$vServerNames">
<td>
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
<!-- Categories with no Name -->
<xsl:for-each select="$vMsg//Category[not(@Name)]/Parameters/Parameter[not(@key = following::Category[not(@Name)]/Parameters/Parameter/@key)]">
<xsl:variable name="vCurrKey" select="@key" />
<tr>
<td>
<xsl:value-of select="$vCurrKey"/>
</td>
<xsl:for-each select="$vServerNames">
<xsl:variable name="vCurrServer" select="text()" />
<xsl:variable name="vCurrVal" select="$vMsg/Results/Data[ServerName = $vCurrServer]/CategorizedData/Category[not(@Name)]/Parameters/Parameter[@key = $vCurrKey]"/>
<xsl:choose>
<xsl:when test="boolean($vCurrVal)">
<td>
<xsl:value-of select="$vCurrVal/@value"/>
</td>
</xsl:when>
<xsl:otherwise>
<td>NA</td>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:for-each>
<!-- Categories with Name -->
<xsl:for-each select="//Category[@Name and not(@Name = following::Category/@Name)]">
<xsl:variable name="vCatName" select="@Name"/>
<tr>
<td>
<xsl:value-of select="$vCatName"/>
</td>
<td/>
<td/>
</tr>
<xsl:variable name="vKeys" select="$vMsg//Category[@Name = $vCatName]/Parameters/Parameter[not(@key = following::Category[@Name = $vCatName]/Parameters/Parameter/@key)]" />
<xsl:for-each select="$vKeys">
<xsl:variable name="vCurrKey" select="@key" />
<tr>
<td>
<xsl:value-of select="$vCurrKey"/>
</td>
<xsl:for-each select="$vServerNames">
<xsl:variable name="vCurrServer" select="text()" />
<xsl:variable name="vCurrVal" select="$vMsg/Results/Data[ServerName = $vCurrServer]/CategorizedData/Category[@Name = $vCatName]/Parameters/Parameter[@key = $vCurrKey]"/>
<xsl:choose>
<xsl:when test="boolean($vCurrVal)">
<td>
<xsl:value-of select="$vCurrVal/@value"/>
</td>
</xsl:when>
<xsl:otherwise>
<td>NA</td>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</xsl:template>
我有一个 xml,如下所示。我正在尝试将此 xml 转换为 html table。 "ServerName" 节点成为列 header。行分组在 "Category" 名称下。当任何 "Data" 节点不存在 "Category->Name" 或 "Parameter->key" 时,它应该显示 "NA".
我尽力自己解决了这个问题,但是我在分组和填充缺失元素方面失败得很惨。非常感谢任何帮助。
<Results>
<Data>
<ServerName>Server1</ServerName>
<CategorizedData>
<Category>
<Parameters>
<Parameter key="A" value="1" />
<Parameter key="B" value="2" />
<Parameter key="C" value="3" />
</Parameters>
</Category>
<Category Name="Misc">
<Parameters>
<Parameter key="A" value="2" />
<Parameter key="B" value="5" />
</Parameters>
</Category>
</CategorizedData>
</Data>
<Data>
<ServerName>Server2</ServerName>
<CategorizedData>
<Category>
<Parameters>
<Parameter key="A" value="10" />
<Parameter key="B" value="20" />
<Parameter key="D" value="30" />
</Parameters>
</Category>
<Category Name="Misc">
<Parameters>
<Parameter key="C" value="2" />
<Parameter key="D" value="5" />
</Parameters>
</Category>
</CategorizedData>
</Data>
</Results>
<table>
<tr>
<td></td>
<td>Server1</td>
<td>Server2</td>
</tr>
<tr>
<td>A</td>
<td>1</td>
<td>10</td>
</tr>
<tr>
<td>B</td>
<td>2</td>
<td>20</td>
</tr>
<tr>
<td>C</td>
<td>3</td>
<td>NA</td>
</tr>
<tr>
<td>D</td>
<td>NA</td>
<td>30</td>
</tr>
<tr>
<td>Misc</td>
<td></td>
<td></td>
</tr>
<tr>
<td>A</td>
<td>2</td>
<td>NA</td>
</tr>
<tr>
<td>B</td>
<td>5</td>
<td>NA</td>
</tr>
<tr>
<td>C</td>
<td>NA</td>
<td>2</td>
</tr>
<tr>
<td>D</td>
<td>NA</td>
<td>5</td>
</tr>
</table>
使用 XSLT 2 或 3,您可以使用嵌套 for-each-group
:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Results">
<table>
<xsl:variable name="servers" select="Data/ServerName"/>
<thead>
<tr>
<th>Key</th>
<xsl:for-each select="$servers">
<th>{.}</th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:for-each-group select=".//Category" group-by="string(@Name)">
<xsl:variable name="cat-group" select="current-group()"/>
<tr>
<th>{current-grouping-key()}</th>
<th>{$servers[1]}</th>
<th>{$servers[2]}</th>
</tr>
<xsl:for-each-group select="current-group()" group-by="Parameters/Parameter/@key">
<tr>
<td>{current-grouping-key()}</td>
<td>{($cat-group[ancestor::Data[ServerName = $servers[1]]]/Parameters/Parameter[@key = current-grouping-key()]/@value, 'N/A')[1]}</td>
<td>{($cat-group[ancestor::Data[ServerName = $servers[2]]]/Parameters/Parameter[@key = current-grouping-key()]/@value, 'N/A')[1]}</td>
</tr>
</xsl:for-each-group>
</xsl:for-each-group>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6qM2e2i/3 给出输出
<table>
<thead>
<tr>
<th>Key</th>
<th>Server1</th>
<th>Server2</th>
</tr>
</thead>
<tbody>
<tr>
<th></th>
<th>Server1</th>
<th>Server2</th>
</tr>
<tr>
<td>A</td>
<td>1</td>
<td>10</td>
</tr>
<tr>
<td>B</td>
<td>2</td>
<td>20</td>
</tr>
<tr>
<td>C</td>
<td>3</td>
<td>N/A</td>
</tr>
<tr>
<td>D</td>
<td>N/A</td>
<td>30</td>
</tr>
<tr>
<th>Misc</th>
<th>Server1</th>
<th>Server2</th>
</tr>
<tr>
<td>A</td>
<td>2</td>
<td>N/A</td>
</tr>
<tr>
<td>B</td>
<td>5</td>
<td>N/A</td>
</tr>
<tr>
<td>C</td>
<td>N/A</td>
<td>2</td>
</tr>
<tr>
<td>D</td>
<td>N/A</td>
<td>5</td>
</tr>
</tbody>
</table>
至于服务器列数的动态,希望适配
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Results">
<table>
<xsl:variable name="servers" select="Data/ServerName"/>
<thead>
<tr>
<th>Key</th>
<xsl:for-each select="$servers">
<th>{.}</th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:for-each-group select=".//Category" group-by="string(@Name)">
<xsl:variable name="cat-group" select="current-group()"/>
<tr>
<th>{current-grouping-key()}</th>
<xsl:for-each select="$servers">
<th>{.}</th>
</xsl:for-each>
</tr>
<xsl:for-each-group select="current-group()" group-by="Parameters/Parameter/@key">
<tr>
<td>{current-grouping-key()}</td>
<xsl:for-each select="$servers">
<td>{($cat-group[ancestor::Data[ServerName = current()]]/Parameters/Parameter[@key = current-grouping-key()]/@value, 'N/A')[1]}</td>
</xsl:for-each>
</tr>
</xsl:for-each-group>
</xsl:for-each-group>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
使用 XSLT 1: 例子: https://xsltfiddle.liberty-development.net/bFukv8n
<?xml version="1.0" encoding="UTF-8"?>
<xsl:template match="/">
<xsl:variable name="vMsg" select="."/>
<xsl:variable name="vServerNames" select="/Results/Data/ServerName" />
<table>
<tr>
<td/>
<xsl:for-each select="$vServerNames">
<td>
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
<!-- Categories with no Name -->
<xsl:for-each select="$vMsg//Category[not(@Name)]/Parameters/Parameter[not(@key = following::Category[not(@Name)]/Parameters/Parameter/@key)]">
<xsl:variable name="vCurrKey" select="@key" />
<tr>
<td>
<xsl:value-of select="$vCurrKey"/>
</td>
<xsl:for-each select="$vServerNames">
<xsl:variable name="vCurrServer" select="text()" />
<xsl:variable name="vCurrVal" select="$vMsg/Results/Data[ServerName = $vCurrServer]/CategorizedData/Category[not(@Name)]/Parameters/Parameter[@key = $vCurrKey]"/>
<xsl:choose>
<xsl:when test="boolean($vCurrVal)">
<td>
<xsl:value-of select="$vCurrVal/@value"/>
</td>
</xsl:when>
<xsl:otherwise>
<td>NA</td>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:for-each>
<!-- Categories with Name -->
<xsl:for-each select="//Category[@Name and not(@Name = following::Category/@Name)]">
<xsl:variable name="vCatName" select="@Name"/>
<tr>
<td>
<xsl:value-of select="$vCatName"/>
</td>
<td/>
<td/>
</tr>
<xsl:variable name="vKeys" select="$vMsg//Category[@Name = $vCatName]/Parameters/Parameter[not(@key = following::Category[@Name = $vCatName]/Parameters/Parameter/@key)]" />
<xsl:for-each select="$vKeys">
<xsl:variable name="vCurrKey" select="@key" />
<tr>
<td>
<xsl:value-of select="$vCurrKey"/>
</td>
<xsl:for-each select="$vServerNames">
<xsl:variable name="vCurrServer" select="text()" />
<xsl:variable name="vCurrVal" select="$vMsg/Results/Data[ServerName = $vCurrServer]/CategorizedData/Category[@Name = $vCatName]/Parameters/Parameter[@key = $vCurrKey]"/>
<xsl:choose>
<xsl:when test="boolean($vCurrVal)">
<td>
<xsl:value-of select="$vCurrVal/@value"/>
</td>
</xsl:when>
<xsl:otherwise>
<td>NA</td>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</xsl:template>