Проблема с сортировкой XSL - PullRequest
3 голосов
/ 10 ноября 2010

У меня проблема с попыткой сортировки с помощью файла XSL с использованием XslCompiledTransform в CLR4.0. Вот мой пример 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 ]

2 голосов
/ 29 декабря 2010

это работает, если изменить версию листа sytyle на '1.0' чего-либо> = 2.0

2 голосов
/ 10 ноября 2010

Подозрительно, если XML для каждого элемента api будет изменен на следующее, результат будет отсортирован, как и ожидалось:

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

Кроме того, если a) XML для каждого элемента 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: Вы можете предварительно обработать XML перед применением XSL

Используйте XSL-преобразование, чтобы переместить значение атрибута id в дочерний элемент api, затем примените тот же XSL, что и в Approach 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>

Надеюсь, это поможет!

1 голос
/ 10 ноября 2010

Я не пытался воспроизвести проблему, и я не вижу ничего плохого в вашем XSL.Вот что я хотел бы узнать, если это ваша проблема или ошибка в движке XSL: попробуйте удалить пробел после <foo> и изменить идентификатор «A» на «C».Получаете ли вы вывод в порядке «B», «C»?Другими словами, убедитесь, что это на самом деле сортировка по id.

0 голосов
/ 15 ноября 2010

@ Расс Ферри, спасибо за ваш ответ.Это указало мне в правильном направлении.Кажется, ошибка в XslCompiledTransform заключается в том, что когда вы хотите отсортировать по атрибуту элемента, он фактически сортирует по значению первого дочернего элемента этого элемента.Как указал Расс, это будет правильно отсортировано с моим исходным файлом преобразования:

<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>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...