2013-02-01 10 views
7

Ciò che era iniziato come una cosa semplice si è rivelato piuttosto problematico per il noob XSLT.Ordinamento XSLT - come ordinare i childcode xml all'interno di un nodo genitore con un attributo

Cercando di ordinare childNodes/discesa ma, dopo l'aggiunta di un attributo al loro nodo genitore, viene visualizzato un errore quando il debug in VS2010:

"Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added."

Supponiamo ho questo semplice XML:

<posts> 
    <year value="2013"> 
     <post postid="10030" postmonth="1"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
     <post postid="10040" postmonth="2"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
     <post postid="10050" postmonth="3"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
    </year> 
    <year value="2012"> 
     <post postid="10010" postmonth="1"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
     <post postid="10015" postmonth="2"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
     <post postid="10020" postmonth="3"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
    </year> 
</posts> 

Ho passato un XPATH a un xmldatasource per recuperare il nodo <year> rilevante, ad es. 2013. Quindi ho bisogno di ordinare i suoi figli con i nodi <post> discendenti usando postid, quindi per <year value=2013>, postid = 10050 verrebbe visualizzato per primo quando sottoposto a rendering.

Quindi, per essere chiari: mi interessa solo l'ordinamento all'interno di un nodo <year>.

Prima ho diviso i nodi in nodi separati (cioè XML è stato/messaggi/post) il seguente XSLT lavorato:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"> 
       <xsl:sort select="@postid" data-type="text" order="descending"/> 
      </xsl:apply-templates> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

Ora la XmlDataSource è vuoto durante l'esecuzione causa l'errore precedente. Se passo ascendente nell'ordine lo stesso xml viene restituito ovviamente (nessuna trasformazione)

Domanda: come aggiornare l'XSLT sopra (o nuovo) per accogliere l'attributo nodo genitore (<year value="">)? Attraverso la ricerca, una risposta ha detto "Ho bisogno di aggiungere la creazione degli attributi prima della creazione degli elementi". Questo ha senso guardando il debugger, i childnodes sono formati in ordine desc, ma il tag year manca il suo attributo. Ma non ho idea di XSLT. Non riesco a vederlo essere troppo complicato ma semplicemente non conosco la lingua.

Qualsiasi aiuto, molto apprezzato. Grazie

risposta

5

Quindi stai dicendo che stai trasmettendo solo parte del tuo documento XML (un nodo <year>) al processore XSLT?

È consigliabile utilizzare un modello separato per anno, in modo che quello sia l'unico modello che utilizza l'ordinamento. Come è la seguente:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="@* | node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@* | node()"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="year"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*" /> 
     <xsl:apply-templates select="post"> 
     <xsl:sort select="@postid" data-type="number" order="descending"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

Credo che quanto sopra è un approccio migliore, ma penso che la causa principale del vostro errore è che si mescolava gli attributi in con gli elementi quando lo ha fatto l'ordinamento. Il tuo XSLT originale probabilmente avrebbe eseguito senza errori se avete fatto semplicemente questo:

<xsl:template match="@*|node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*"> 
     <xsl:apply-templates select="node()"> 
      <xsl:sort select="@postid" data-type="text" order="descending"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 
+0

Questo è tutto. Fantastico funziona. Grazie molte signore! Sì, sto passando solo un nodo di un anno all'XSLT. Scelgo l'anno/i post che voglio mostrare. Cercherà di capire la tua nuova struttura e imparare a leggere xslt. – JimXC

+0

Perché si dovrebbe usare 'data-type =" text "' in questo caso? questo ovviamente produce risultati errati se i valori di 'postid' possono essere di lunghezza diversa. –

+0

Questo è un buon punto. L'ho usato perché è quello che usava l'XSLT originale di JimXC e non era il fulcro della sua domanda, inoltre non avevo notato che gli ID nel suo esempio erano numerici. Se gli ID sono garantiti come numerici, sarebbe preferibile 'data-type =" numeric "' – JLRishe

2

Questa trasformazione:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="year"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*"/> 
    <xsl:apply-templates select="*"> 
     <xsl:sort select="@postid" data-type="number" order="descending"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

se applicato sul documento XML fornito:

<posts> 
    <year value="2013"> 
     <post postid="10030" postmonth="1"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
     <post postid="10040" postmonth="2"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
     <post postid="10050" postmonth="3"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
    </year> 
    <year value="2012"> 
     <post postid="10010" postmonth="1"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
     <post postid="10015" postmonth="2"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
     <post postid="10020" postmonth="3"> 
      <othernode></othernode> 
      <othernode2></othernode2> 
     </post> 
    </year> 
</posts> 

produce il risultato desiderato e corretto:

<posts> 
    <year value="2013"> 
     <post postid="10050" postmonth="3"> 
     <othernode/> 
     <othernode2/> 
     </post> 
     <post postid="10040" postmonth="2"> 
     <othernode/> 
     <othernode2/> 
     </post> 
     <post postid="10030" postmonth="1"> 
     <othernode/> 
     <othernode2/> 
     </post> 
    </year> 
    <year value="2012"> 
     <post postid="10020" postmonth="3"> 
     <othernode/> 
     <othernode2/> 
     </post> 
     <post postid="10015" postmonth="2"> 
     <othernode/> 
     <othernode2/> 
     </post> 
     <post postid="10010" postmonth="1"> 
     <othernode/> 
     <othernode2/> 
     </post> 
    </year> 
</posts> 
+0

Confermato anche questo funziona. Ancora, a mio vantaggio, stai usando il numero nel tipo di dati - ok capito. Ma il poster precedente usato select = post nel template apply, tu usi *. Qual è la differenza? Supponendo che ti stai occupando di nodi con nome mentre altri sono post specifici? – JimXC

+1

@JimXC, Ogni volta che sappiamo che ogni elemento figlio del nodo corrente è un 'post', usare' * 'potrebbe essere più efficiente in quanto evita un controllo da parte del processore XSLT se un bambino è un' post' o meno. Inoltre, il codice è più breve in questo modo e la scrittura è più veloce e più facile. –

+0

Capito grazie. Non posso alzare la voce o +1 sì, ho paura. – JimXC

Problemi correlati