代码之家  ›  专栏  ›  技术社区  ›  Mike Dour

XSL排序问题

  •  3
  • Mike Dour  · 技术社区  · 14 年前

    我在尝试使用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>
    

    但是,如果我在第二个 < 元素,结果文件将正确排序。这看起来可能是XslCompiledTransform中的一个bug,但我希望有人能找到解决方法。

    编辑:如果有人在复制时遇到问题,这里是我使用的代码:

    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 回复  |  直到 14 年前
        1
  •  2
  •   Russ Ferri    14 年前

    可疑的是,如果每个 api

    <api id="apiId">
       <id>apiId</id>
       <foos>
          <foo />
       </foo>       
    </api>
    

    此外,如果a)每个 应用程序编程接口 id 完全属性

    <api>
       <id>apiId</id>
       <foos>
          <foo />
       </foo>       
    </api>
    

    b)只有 第二 @id 在XSL文件中更改为 身份证件


    有可能 XslCompiledTransform 正在尝试对名为的子元素进行排序 身份证件 身份证件 ,否则这可能只是愚蠢的运气。不管怎样,我已经验证了它是否愿意对名为 .

    考虑到这一点,我可以考虑两种解决方法,但这两种方法都要求您对转换过程有一定程度的控制。

    方法1:您可以更改XML

    更改写入原始XML的进程以指定 作为 元素。然后更新XSL以替换对 @身份证 身份证件 .

    方法2:在应用XSL之前,可以预先处理XML

    使用XSL转换移动 身份证件 属性的子元素 应用程序编程接口 ,然后应用与中相同的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>
    

    我希望这有帮助!

        2
  •  2
  •   Hubery    14 年前

    如果将sytyle工作表版本更改为任何内容的“1.0”>=2.0,则此操作有效

        3
  •  1
  •   Don Kirkby    14 年前

    <foo> 把id“A”改成“C”。你的输出是按“B”、“C”的顺序吗?换句话说,确保它实际上是按id排序的。

        4
  •  0
  •   Mike Dour    14 年前

    @罗斯费里,谢谢你的回答。它给我指明了正确的方向。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>