Вопрос

У меня возникла проблема, пытаясь сортировать с помощью файла 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

@Russ ferri, спасибо за ваш ответ. Это указало меня в правильном направлении. Похоже, что ошибка в 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> элементы

Другие советы

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

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

Кроме того, если A) XML для каждого api Элемент изменен, чтобы удалить id Атрибут целиком

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

и б) только второй ссылка на @id В файле XSL изменяется на id, Результат все еще будет сортироваться в алфавитном порядке!


Возможно, что XslCompiledTransform пытается разобраться на элементе ребенка по имени id вместо атрибута с именем id, или это может быть просто глупаться. В любом случае, я проверил, он готов корректно сортировать на дочернем элементе имени id.

Имея это в виду, я могу придумать две обходные пути, но оба требуют, чтобы у вас был какой-то уровень контроля над процессом преобразования.

Подход 1: вы можете изменить XML

Измените процесс, написав оригинал XML для уточнения id как первый элемент, содержащийся api элемент. Затем обновите XSL, чтобы заменить ссылки на @id с участием id.

Подход 2: вы можете предварительно обработать XML перед применением вашего XSL

Используйте преобразование 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 Lists на «1.0» чего-либо> = 2.0

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top