题
我遇到了一个问题,试图使用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);
}
}
解决方案 4
@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>
元素
其他提示
可疑的,如果每个XML api
元素已修改为以下内容,结果将按预期进行排序:
<api id="apiId">
<id>apiId</id>
<foos>
<foo />
</foo>
</api>
此外,如果a)每个 api
修改元素以删除 id
完全属性
<api>
<id>apiId</id>
<foos>
<foo />
</foo>
</api>
b)只有 第二 引用 @id
在XSL中,文件已更改为 id
, ,结果仍将按字母顺序排序!
可能 XslCompiledTransform
正在尝试分类一个名为的子元素 id
而不是命名的属性 id
, ,否则这可能只是愚蠢的运气。无论哪种方式,我都验证了它愿意在名为的子元素上正确排序 id
.
考虑到这一点,我可以想到两个解决方法,但两者都要求您对转换过程有一定程度的控制。
方法1:您能够更改XML
更改编写原始XML的过程以指定 id
作为第一个元素包含 api
元素。然后更新XSL以替换引用到 @id
和 id
.
方法2:在应用XSL之前,您可以预处理XML
使用XSL变换移动 id
将属性归因于子元素 api
, ,然后应用与您中相同的XSL 方法1 到中间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>
我希望这有帮助!
如果将sytyle表版本更改为任何> = 2.0的'1.0',它有效
我没有试图重现问题,而且您的XSL显然没有任何错误。这是我要探索的内容,看看是您的问题还是XSL引擎中的错误:尝试删除空间 <foo>
并将ID“ A”更改为“ C”。您是否以“ B”,“ C”顺序获得输出?换句话说,请确保它实际上是按ID进行排序。