È xsl: sequenza di sempre non vuota?
-
11-09-2019 - |
Domanda
Non capisco uscita da questo foglio di stile:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="root/sub"/>
</xsl:template>
<xsl:template match="sub">
<xsl:variable name="seq">
<xsl:sequence select="*" />
</xsl:variable>
<xsl:message>
<xsl:value-of select="@id" />
<xsl:text>: </xsl:text>
<xsl:value-of select="count($seq)" />
</xsl:message>
</xsl:template>
</xsl:stylesheet>
se applicato a XML seguente:
<root>
<sub id="empty" />
<sub id="one"><one/></sub>
<sub id="two"><one/><one/></sub>
<sub id="three"><one/><one/><one/></sub>
</root>
Output, scritto da elemento xsl:message
, è:
empty: 1
one: 1
two: 1
three: 1
Mi aspettavo questo uno invece:
empty: 0
one: 1
two: 2
three: 3
Perché count($seq)
sempre ritornare 1 in questo caso? Come cambieresti definizione di variabile, in modo che possa poi testarlo per il vuoto? (Simple <xsl:variable name='seq' select='*' />
sarebbe tornare risposta attesa, ma non è un'opzione ... voglio cambiare variabile between
in questo modello e testarlo per il vuoto in seguito).
Soluzione
Vorrei provare a rispondere al "perché" parte della tua domanda.
Se si scrive la seguente dichiarazione:
<xsl:variable name="x" select="*" />
il $x
variabile contiene la sequenza dei nodi figli del nodo corrente. Non c'è nessun nodo padre implicita $x
, perché si utilizzano select
. Consideriamo ora il seguente:
<xsl:variable name="x">
<content />
<content />
</xsl:variable>
dove $x
variabile contiene una sequenza di un nodo: il nodo padre content
. Qui, count($x)
sarà sempre darà 1
. Per ottenere la quantità di elementi content
, è necessario contare i figli del nodo radice implicita $x
: count($x/content)
Come regola generale, si può ricordare che: se xsl:variable
è di per sé un elemento vuoto con un attributo select
, il set di risultati verrà assegnato alla variabile. Se si utilizza xsl:variable
senza l'attributo select
, si crea sempre un genitore implicita con la variabile, e il contenuto della variabile siete i figli sotto di esso .
Lo stesso vale per il tuo esempio con xsl:sequence
come un bambino a xsl:variable
. Avendo un xsl:variable
non vuota si crea un genitore implicita per la sequenza, che è il motivo per cui si ottiene sempre 1
se si conta la variabile stessa.
Se avete bisogno di entrambe le cose: un fascio di istruzioni all'interno xsl:variable
, ma il comportamento dell'attributo select
, è possibile risolvere questo utilizzando il seguente:
<xsl:variable name="x" select="$y/*" />
<xsl:variable name="y">
<xsl:sequence select="foo" />
</xsl:variable>
che ora produrrà la quantità prevista per count($x)
, ma se non che è davvero utile o rende il codice più chiaro è discutibile.
Altri suggerimenti
Funziona come ci si potrebbe aspettare se si sia modificare la dichiarazione variabile:
<xsl:variable name="seq" select="*"/>
o dichiarare il tipo della variabile usando 'come' attributo:
<xsl:variable name="seq" as="item()*">
<xsl:sequence select="*" />
</xsl:variable>
Non specificare le informazioni di tipo spesso Leeds a risultati sorprendenti in XSLT 2.0. Se si utilizza sassone, è possibile uscita come Saxon interpreta il foglio di stile utilizzando l'attributo di estensione spiegare:
<xsl:template match="sub" saxon:explain="yes" xmlns:saxon="http://saxon.sf.net/">
<xsl:variable name="seq">
<xsl:sequence select="*" />
</xsl:variable>
<xsl:message>
<xsl:value-of select="@id" />
<xsl:text>: </xsl:text>
<xsl:value-of select="count($seq)" />
</xsl:message>
</xsl:template>
Come si può vedere, sassone costruisce un documento-nodo di fuori della sequenza:
Optimized expression tree for template at line 6 in :
let $seq[refCount=1] as document-node() :=
document-constructor
child::element()
return
message
Si sta selezionando i nodi secondari, e poi contando ogni nodo - in modo che sarai sempre 1. È necessario contare i bambini, per esempio:
<xsl:value-of select="count($seq/*)" />
vi darà l'output vi aspettate.