Question

J'ai un problème en essayant de trier avec un fichier XSL en utilisant le XslCompiledTransform dans CLR4.0. Voici mon exemple de fichier XML (Note: il y a un espace après le deuxième élément de <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>

Quand je demande le fichier XSL suivant:

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

J'obtenir le résultat suivant:

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

Cependant, si je retire l'espace après le deuxième élément de <foo>, le fichier obtenu est triée correctement. Cela semble que c'est probablement un bug dans le XslCompiledTransform, mais j'espérais que quelqu'un pourrait avoir une solution de contournement.

Edit: Si quelqu'un a de la difficulté à reproduire, voici le code que je suis en utilisant:

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);
    }
}
Était-ce utile?

La solution 4

@Russ Ferri, merci pour votre réponse. Il m'a orienté dans la bonne direction. Il semble que le bug dans le XslCompiledTransform est que lorsque vous voulez trier par un attribut d'un élément, il trie en fait par la valeur du premier élément enfant de cet élément. Alors que Russ a fait remarquer, cela sorte correctement avec mon fichier de transformation d'origine:

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

Mais il en sera de ceci:

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

En fait, le nom d'attribut est complètement ignoré, donc si je lance la transformation sur quelque chose comme ceci:

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

Les regards de résultat comme celui-ci:

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

Quel est le tri correct si vous triez les éléments <anyElementName>

Autres conseils

soupçonneux si le XML pour chaque élément de api est modifié pour ce qui suit, le résultat sera trié comme prévu:

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

En outre, si a) le XML pour chaque élément de api est modifié pour supprimer le tout attribut id

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

et b) que le secondes référence à @id dans le fichier XSL est changé en id, le résultat sera toujours classés par ordre alphabétique!


Il est possible que le XslCompiledTransform tente de trier sur un élément enfant nommé id au lieu d'un attribut nommé id, ou cela pourrait juste être stupide chance. De toute façon, je l'ai vérifié qu'il est prêt à trier correctement sur un élément enfant nommé id.

Dans cet esprit, je peux penser à deux solutions de contournement, mais les deux exigent que vous avez un certain niveau de contrôle sur le processus de transformation.

Approche 1: Vous êtes en mesure de changer le XML

Modifier le processus d'écriture du XML d'origine pour spécifier le id comme premier élément contenu par un élément de api. Ensuite, mettre à jour le XSL pour remplacer les références à @id avec id.

Approche 2: Vous êtes en mesure de pré-traiter le XML avant d'appliquer votre XSL

Utilisez une transformation XSL pour déplacer la valeur de l'attribut id dans un élément enfant de api, puis appliquer la même XSL vous Approche 1 au document XML intermédiaire. Transformer le document deux fois serait évidemment moins souhaitable lors du traitement des fichiers XML volumineux.

Le suivant vous XSL obtenir à partir du XML d'origine vers le XML intermédiaire:

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

J'espère que cela aide!

il fonctionne si le changement de la version de la feuille de sytyle à '1,0' de quoi que ce soit> = 2.0

Je n'ai pas essayé de reproduire le problème, et je ne vois rien évidemment mal avec votre XSL. Voici ce que j'explorerais pour voir si c'est votre problème ou un bug dans le moteur XSL: essayez de supprimer l'espace après <foo> et le changement id « A » à « C ». Avez-vous la sortie dans l'ordre « B », « C »? En d'autres termes, assurez-vous qu'il est en fait le tri par id.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top