XSL 排序问题
我在尝试使用 CLR4.0 中的 XslCompiledTransform 对 XSL 文件进行排序时遇到问题。这是我的示例 XML 文件(注意:第二个
元素后面有一个空格):
<?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>
但是,如果我删除如果第二个
元素后面有空格,则结果文件排序正确。这看起来可能是 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);
}
}
I am having a problem trying to sort with an XSL file using the XslCompiledTransform in CLR4.0. Here is my sample XML file (Note: there is a space after the second <foo>
element):
<?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>
When I apply the following XSL file:
<?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>
I get the following result:
<html>
<body>
<table>
<tr>
<td>B</td>
</tr>
<tr>
<td>A</td>
</tr>
</table>
</body>
</html>
However, if I remove the space after the second <foo>
element, the resulting file is sorted correctly. This seems like it's probably a bug in the XslCompiledTransform, but I was hoping someone might have a workaround.
Edit: If anyone is having trouble reproducing it, here is the code I am using:
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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
可疑的是,如果每个
api
元素的 XML 修改为以下内容,结果将按预期排序:此外,如果 a) 每个
api
元素的 XML 被修改完全删除id
属性,并且 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:
我希望这会有所帮助!
Suspiciously, if the XML for each
api
element is modified to the following, the result will be sorted as expected:Furthermore, if a) the XML for each
api
element is modified to remove theid
attribute entirelyand b) only the second reference to
@id
in the XSL file is changed toid
, the result will still be sorted alphabetically!It is possible that the
XslCompiledTransform
is attempting to sort on a child element namedid
instead of an attribute namedid
, or this could just be dumb luck. Either way, I've verified it is willing to sort correctly on a child element namedid
.With this in mind, I can think of two workarounds, but both require that you have some level of control over the transform process.
Approach 1: You are able to change the XML
Change the process writing the original XML to specify the
id
as the first element contained by anapi
element. Then update the XSL to replace references to@id
withid
.Approach 2: You are able to pre-process the XML before applying your XSL
Use an XSL transform to move the value of the
id
attribute into a child element ofapi
, then apply the same XSL as you would in Approach 1 to the intermediate XML document. Transforming the document twice would obviously be less desirable when processing large XML files.The following XSL will get you from the original XML to the intermediate XML:
I hope this helps!
如果将样式表版本更改为任何> = 2.0 的“1.0”,它就可以工作
it works if change the sytyle sheet version to '1.0' of anything >= 2.0
我没有尝试重现该问题,并且我没有发现您的 XSL 有任何明显的问题。以下是我要探索的内容,看看这是您的问题还是 XSL 引擎中的错误:尝试删除
后面的空格并将 id“A”更改为“C”。你得到的输出顺序是“B”、“C”吗?换句话说,确保它实际上是按 id 排序的。I haven't tried to reproduce the problem, and I don't see anything obviously wrong with your XSL. Here's what I would explore to see if it's your problem or a bug in the XSL engine: try removing the space after
<foo>
and change id "A" to "C". Do you get the output in the order "B", "C"? In other words, make sure that it's actually sorting by id.@Russ Ferri,感谢您的回答。它为我指明了正确的方向。 XslCompiledTransform 中的错误似乎是,当您想要按元素的属性排序时,它实际上是按该元素的第一个子元素的值排序。因此,正如 Russ 指出的那样,这将与我的原始转换文件正确排序:
但是这也会正确排序:
事实上,属性名称完全被忽略,所以如果我对这样的东西运行转换:
结果看起来像这样
:如果您按
元素排序,则正确的排序@Russ Ferri, thanks for your answer. It pointed me in the right direction. It appears the bug in the XslCompiledTransform is that when you want to sort by an attribute of an element, it actually sorts by the value of the first child element of that element. So as Russ pointed out, this will sort correctly with my original transform file:
But so will this:
In fact, the attribute name is completely ignored, so if I run the transform on something like this:
The result looks like this:
Which is the correct sorting if you were sorting by the
<anyElementName>
elements