Pregunta

Estoy teniendo un problema al intentar clasificar con un archivo XSL utilizando el XslCompiledTransform en CLR4.0. Aquí está mi archivo XML de ejemplo (Nota: hay un espacio después del segundo elemento <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>

Cuando aplico el siguiente archivo 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>

Me sale el siguiente resultado:

<html>
  <body>
    <table>
      <tr>
        <td>B</td>
      </tr>
      <tr>
        <td>A</td>
      </tr>
    </table>
  </body>
</html>

Sin embargo, si quito el espacio después del segundo elemento <foo>, el archivo resultante se ordena correctamente. Esto parece que es probablemente un error en el XslCompiledTransform, pero yo estaba esperando que alguien podría tener una solución.

Editar: Si alguien está teniendo problemas para reproducirlo, aquí está el código que estoy utilizando:

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);
    }
}
¿Fue útil?

Solución 4

@Russ Ferri, gracias por su respuesta. Se me señaló en la dirección correcta. Al parecer, el error en el XslCompiledTransform es que cuando se desea ordenar un atributo de un elemento, en realidad se ordena por el valor del primer elemento secundario de ese elemento. Así como Russ señaló, esto va a clasificar correctamente con mi original archivo de transformación:

<reflection>
  <apis>
    <api id="C">
      <id>C</id>
      <foos>
        <foo/>
      </foos>
    </api>
    <api id="B">
      <id>B</id>
      <foos>
        <foo/>
      </foos>
    </api>
  </apis>
</reflection>

Pero también lo será la siguiente:

<reflection>
  <apis>
    <api id="C">
      <anyElementName>C</anyElementName>
      <foos>
        <foo/>
      </foos>
    </api>
    <api id="B">
      <anyElementName>B</anyElementName>
      <foos>
        <foo/>
      </foos>
    </api>
  </apis>
</reflection>

De hecho, el nombre del atributo se ignora por completo, por lo que si corro el de transformación en algo como esto:

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

El resultado es similar al siguiente:

<html>
  <body>
    <table>
      <tr>
        <td>B</td>
      </tr>
      <tr>
        <td>A</td>
      </tr>
      <tr>
        <td>C</td>
      </tr>
    </table>
  </body>
</html>

¿Cuál es la clasificación correcta si estuviera ordenando con los elementos <anyElementName>

Otros consejos

con sospecha, si el XML para cada elemento api se modifica a lo siguiente, el resultado será clasificada como era de esperar:

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

Además, si a) el XML para cada elemento api se modifica para quitar el atributo id enteramente

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

y b) sólo el segundo referencia a @id en el archivo XSL se cambia a id, el resultado seguirá siendo organizados alfabéticamente!


Es posible que el XslCompiledTransform está tratando de resolver en un elemento secundario llamado id en lugar de un atributo llamado id, o esto podría ser sólo pura suerte. De cualquier manera, he verificado que está dispuesto para ordenar correctamente en un elemento secundario llamado id.

Con esto en mente, se me ocurren dos soluciones, pero ambos requieren que tenga un cierto nivel de control sobre el proceso de transformación.

Enfoque 1: Usted es capaz de cambiar el código XML

Cambiar el proceso de escritura del original XML para especificar el id como el primer elemento de contenido por un elemento api. A continuación, actualice el XSL para reemplazar las referencias a @id con id.

Enfoque 2: Usted es capaz de pre-procesar el XML antes de aplicar el XSL

El uso de una transformación XSL para mover el valor del atributo id en un elemento secundario de api, a continuación, aplicar el mismo XSL como lo haría en Enfoque 1 para el documento XML intermedio. Transformando el documento dos veces, obviamente, sería menos deseable cuando el procesamiento de grandes archivos XML.

La siguiente XSL le conseguirá desde el XML original al XML intermedio:

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

Espero que esto ayude!

funciona si el cambio de la versión hoja sytyle a '1.0' de nada> = 2,0

No he tratado de reproducir el problema, y ??no ver nada, obviamente mal con su XSL. Esto es lo que iba a explorar para ver si es su problema o un error en el motor XSL: Trate de no incluir el espacio después de <foo> y el cambio id "A" a "C". Qué se obtiene la salida en el orden de "B", "C"? En otras palabras, asegúrese de que se trata en realidad de clasificación por id.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top