我正试图将一些XML输出转换为一个评级表(与水文相关),该表由顶部的百分之一值和底部的十分之一值组成。我遇到的问题是,我似乎无法通过在ht属性上使用子字符串来对唯一值进行排序。
以下是XML输出的示例:
<env:Envelope>
<env:Header/>
<env:Body>
<RatingTableUpdate>
<Gauge xmlns="" type="STORAGE" id="123">
<Input input="0">
<Conv ht="19.76" cap="5040" />
<Conv ht="19.77" cap="5045" />
<Conv ht="19.78" cap="5051" />
<Conv ht="19.79" cap="5057" />
<Conv ht="19.80" cap="5063" />
<Conv ht="19.81" cap="5069" />
<Conv ht="19.82" cap="5074" />
<Conv ht="19.83" cap="5080" />
<Conv ht="19.84" cap="5086" />
<Conv ht="19.85" cap="5092" />
<Conv ht="19.86" cap="5098" />
<Conv ht="19.87" cap="5104" />
<Conv ht="19.88" cap="5109" />
<Conv ht="19.89" cap="5115" />
<Conv ht="19.90" cap="5121" />
<Conv ht="19.91" cap="5127" />
<Conv ht="19.92" cap="5133" />
<Conv ht="19.93" cap="5138" />
</Input>
</Gauge>
</RatingTableUpdate>
</env:Body>
</env:Envelope>
输出应该是这样的:
GH 0.00 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09
19.7 5040 5045 5051 5057
19.8 5063 5069 5074 5080 5086 5092 5098 5104 5109 5115
19.9 5121 5127 5133 5138
我可以在正确的列中获得cap属性,但不能使GH列(去掉百分之一值的ht属性)每行只显示一次。我得到这样的东西:
GH 0.00 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09
19.7 5040
5045
5051
5057
19.8 5063
5069
5074
5080
5086
5092
5098
5104
5109
5115
19.9 5121
5127
5133
5138
这是xsl中格式化该部分的部分。xsl:choot似乎适用于列。我尝试过Muenchian方法,但不能完全正确(很难让它与子字符串键一起工作)。
这是最好的方法吗?还是有其他方法可以让每一行只为给定的子字符串显示一次(19.76、19.77、19.78、19.79值显示19.7次)评级通常以非零值开始,就像示例一样,所以我不能只使用第一百个0值来开始一个组,而使用9来结束一个组。
<xsl:for-each select="env:Envelope/env:Body/RatingTableUpdate/Gauge">
<table width="100%">
<tr class="site">
<td width="150px">Site ID: <u><xsl:value-of select="@id" /></u></td>
<td>Site Name: <u><xsl:value-of select="@idname" /></u></td>
<td>Type: <u><xsl:value-of select="@type" /></u></td>
</tr>
</table>
<table width="100%">
<tr class="tbldata">
<th rowspan="1" class="tbldataheader">GH</th>
<th rowspan="1" class="tbldataheader">0.00</th>
<th rowspan="1" class="tbldataheader">0.01</th>
<th rowspan="1" class="tbldataheader">0.02</th>
<th rowspan="1" class="tbldataheader">0.03</th>
<th rowspan="1" class="tbldataheader">0.04</th>
<th rowspan="1" class="tbldataheader">0.05</th>
<th rowspan="1" class="tbldataheader">0.06</th>
<th rowspan="1" class="tbldataheader">0.07</th>
<th rowspan="1" class="tbldataheader">0.08</th>
<th rowspan="1" class="tbldataheader">0.09</th>
</tr>
<xsl:for-each select="./Input/Conv">
<tr class="tbldata">
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 0">
<td class="ifrtext"><xsl:value-of select="substring(@ht,1,string-length(@ht)-1)" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 0">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 1">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 2">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 3">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 4">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 5">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 6">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 7">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 8">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="substring(@ht,string-length(@ht),1) = 9">
<td class="ifrtext"><xsl:value-of select="@cap" /></td>
</xsl:when>
<xsl:otherwise>
<td class="ifrtext"></td>
</xsl:otherwise>
</xsl:choose>
</tr>
</xsl:for-each>
</table>
</xsl:for-each>
谢谢,Matt
此转换:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kValByTenths" match="Conv"
use="substring(@ht, 1, 4)"/>
<xsl:key name="kCapFromHt" match="@cap"
use="../@ht"/>
<xsl:template match="Input">
<table>
<xsl:for-each select=
"Conv[generate-id()
=
generate-id(key('kValByTenths',substring(@ht, 1, 4))[1])
]">
<xsl:variable name="vKey" select="substring(@ht, 1, 4)"/>
<tr>
<td><xsl:value-of select="$vKey"/></td>
<xsl:for-each select=
"(//node() | //namespace::*)[not(position() > 10)]">
<xsl:variable name="vPos" select="position()"/>
<td>
<xsl:value-of select="key('kCapFromHt', concat($vKey,$vPos -1))"/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
当应用于所提供的XML文档时(更正为格式良好的XML文档):
<env:Envelope xmlns:env="some:env">
<env:Header/>
<env:Body>
<RatingTableUpdate>
<Gauge xmlns="" type="STORAGE" id="123">
<Input input="0">
<Conv ht="19.76" cap="5040" />
<Conv ht="19.77" cap="5045" />
<Conv ht="19.78" cap="5051" />
<Conv ht="19.79" cap="5057" />
<Conv ht="19.80" cap="5063" />
<Conv ht="19.81" cap="5069" />
<Conv ht="19.82" cap="5074" />
<Conv ht="19.83" cap="5080" />
<Conv ht="19.84" cap="5086" />
<Conv ht="19.85" cap="5092" />
<Conv ht="19.86" cap="5098" />
<Conv ht="19.87" cap="5104" />
<Conv ht="19.88" cap="5109" />
<Conv ht="19.89" cap="5115" />
<Conv ht="19.90" cap="5121" />
<Conv ht="19.91" cap="5127" />
<Conv ht="19.92" cap="5133" />
<Conv ht="19.93" cap="5138" />
</Input>
</Gauge>
</RatingTableUpdate>
</env:Body>
</env:Envelope>
生成想要的(没有装饰),正确的结果:
<table>
<tr>
<td>19.7</td>
<td/>
<td/>
<td/>
<td/>
<td/>
<td/>
<td>5040</td>
<td>5045</td>
<td>5051</td>
<td>5057</td>
</tr>
<tr>
<td>19.8</td>
<td>5063</td>
<td>5069</td>
<td>5074</td>
<td>5080</td>
<td>5086</td>
<td>5092</td>
<td>5098</td>
<td>5104</td>
<td>5109</td>
<td>5115</td>
</tr>
<tr>
<td>19.9</td>
<td>5121</td>
<td>5127</td>
<td>5133</td>
<td>5138</td>
<td/>
<td/>
<td/>
<td/>
<td/>
<td/>
</tr>
</table>
解释:
正确使用Muenchian分组方法,其中
Conv
元素由其ht
属性的字符串vaue的前4个字符索引。正确使用Piez方法进行N次非递归迭代。
更新:
OP报告说,由于MSXML3中的一个错误,他在使用IE8时无法获得正确的结果。
下面是一个经过修改的解决方案,它也可以与MSXML3/IE 8:一起使用
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kValByTenths" match="Conv"
use="substring(@ht, 1, 4)"/>
<xsl:variable name="vConvs" select="//Conv"/>
<xsl:template match="Input">
<table>
<xsl:for-each select=
"Conv[generate-id()
=
generate-id(key('kValByTenths',substring(@ht, 1, 4))[1])
]">
<xsl:variable name="vKey" select="substring(@ht, 1, 4)"/>
<tr>
<td><xsl:value-of select="$vKey"/></td>
<xsl:for-each select=
"(//node() | //namespace::*)[not(position() > 10)]">
<xsl:variable name="vPos" select="position()"/>
<td>
<xsl:value-of select=
"$vConvs[substring-after(@ht, $vKey) = $vPos]/@cap"/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>