我在使用CLR4.0中的XslCompiledTransform尝试使用XSL文件进行排序时遇到问题。这是我的示例XML文件(注意:第二个<foo>
元素后面有一个空格):
<?xml version="1.0" encoding="utf-8"?>
<reflection>
<apis>
<api id="A">
<foos>
<foo/>
</foos>
</api>
<api id="B">
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
当我应用以下XSL文件时:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:apply-templates select="/reflection/apis/api">
<xsl:sort select="@id" />
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="api">
<tr>
<td>
<xsl:value-of select="@id" />
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
我得到以下结果:
<html>
<body>
<table>
<tr>
<td>B</td>
</tr>
<tr>
<td>A</td>
</tr>
</table>
</body>
</html>
但是,如果我在第二个<foo>
元素之后删除空格,则会正确排序生成的文件。这似乎可能是XslCompiledTransform中的一个错误,但我希望有人可能有一个解决方法。
编辑:如果有人在复制时遇到问题,这里是我正在使用的代码:
XslCompiledTransform xslt = new XslCompiledTransform();
XsltSettings transformSettings = new XsltSettings(true, true);
xslt.Load("CreateVSToc.xsl", transformSettings, new XmlUrlResolver());
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreWhitespace = true;
Stream readStream = File.Open("reflection.xml", FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
using (XmlReader reader = XmlReader.Create(readStream, readerSettings))
{
Stream outputStream = File.Open("toc.xml", FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete);
using (XmlWriter writer = XmlWriter.Create(outputStream, xslt.OutputSettings))
{
XsltArgumentList arguments = new XsltArgumentList();
xslt.Transform(reader, arguments, writer);
}
}
答案 0 :(得分:2)
可疑的是,如果将每个api
元素的XML修改为以下内容,则结果将按预期排序:
<api id="apiId">
<id>apiId</id>
<foos>
<foo />
</foo>
</api>
此外,如果a)修改每个api
元素的XML以完全删除id
属性
<api>
<id>apiId</id>
<foos>
<foo />
</foo>
</api>
和b)只有XSL文件中对@id
的第二个引用更改为id
,结果仍将按字母顺序排序!
XslCompiledTransform
可能正在尝试对名为id
的子元素进行排序,而不是名为id
的属性,或者这可能只是运气不好。不管怎样,我已经验证它愿意在名为id
的子元素上正确排序。
考虑到这一点,我可以想到两个解决方法,但两者都要求您对转换过程有一定程度的控制。
方法1:您可以更改XML
更改编写原始XML的过程,以将id
指定为api
元素包含的第一个元素。然后更新XSL以使用@id
替换对id
的引用。
方法2:您可以在应用XSL之前预处理XML
使用XSL转换将id
属性的值移动到api
的子元素中,然后将方法1 中的相同XSL应用于中间XML文档。在处理大型XML文件时,显然不太需要将文档转换两次。
以下XSL将使您从原始XML转到中间XML:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1">
<!-- recursively copy each element (including root) -->
<xsl:template match="*|/">
<xsl:copy>
<!-- xsl:copy ignores attributes, copy those as well -->
<xsl:copy-of select="@*"/>
<!-- continue to deep copy the element -->
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- for api elements, move the id attribute into an element -->
<xsl:template match="api">
<api>
<id>
<xsl:value-of select="@id"/>
</id>
<!-- continue deep copy of api element contents -->
<xsl:apply-templates />
</api>
</xsl:template>
</xsl:stylesheet>
我希望这有帮助!
答案 1 :(得分:2)
如果将样式表版本更改为任何&gt; = 2.0
的任何内容,则可以使用它答案 2 :(得分:1)
我没有尝试重现这个问题,我没有看到你的XSL明显错误。以下是我将要探索的问题,看看它是您的问题还是XSL引擎中的错误:尝试删除<foo>
之后的空格并将ID“A”更改为“C”。您是按“B”,“C”的顺序得到输出的吗?换句话说,确保它实际上按id排序。
答案 3 :(得分:0)
@Russ Ferri,谢谢你的回答。它指出了我正确的方向。看来XslCompiledTransform中的错误是当你想要按元素的属性排序时,它实际上按该元素的第一个子元素的值排序。正如Russ指出的那样,这将使用我原来的转换文件正确排序:
<reflection>
<apis>
<api id="C">
<id>C</id>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<id>B</id>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
但是这样:
<reflection>
<apis>
<api id="C">
<anyElementName>C</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<anyElementName>B</anyElementName>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
实际上,属性名称被完全忽略,所以如果我在这样的事情上运行转换:
<reflection>
<apis>
<api id="C">
<anyElementName>Z</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="A">
<anyElementName>Y</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<anyElementName>X</anyElementName>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
结果如下:
<html>
<body>
<table>
<tr>
<td>B</td>
</tr>
<tr>
<td>A</td>
</tr>
<tr>
<td>C</td>
</tr>
</table>
</body>
</html>
如果按<anyElementName>
元素