XSL排序问题

时间:2010-11-10 01:08:42

标签: .net xml xslt xslcompiledtransform

我在使用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 个答案:

答案 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>元素

进行排序,则哪种排序正确