É xsl: seqüência sempre não-vazia?
-
11-09-2019 - |
Pergunta
Eu não entendo saída desse estilo:
<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>
quando aplicada a seguinte XML:
<root>
<sub id="empty" />
<sub id="one"><one/></sub>
<sub id="two"><one/><one/></sub>
<sub id="three"><one/><one/><one/></sub>
</root>
Saída, escrito por elemento xsl:message
, é:
empty: 1
one: 1
two: 1
three: 1
Eu esperava este em vez disso:
empty: 0
one: 1
two: 2
three: 3
Por que count($seq)
sempre retornar 1 neste caso? Como você mudaria definição de variável, para que eu possa depois testá-lo para o vazio? (<xsl:variable name='seq' select='*' />
simples voltaria resposta esperada, mas não é uma opção ... Eu quero variável mudança between
em este modelo , e testá-lo para o vazio mais tarde).
Solução
Deixe-me tentar responder o "porquê" parte da sua pergunta.
Se você escrever a seguinte declaração:
<xsl:variable name="x" select="*" />
o $x
variável contém a sequência dos nós filhos do nó atual. Não há nenhum nó pai implícita na $x
, porque você usa select
. Agora, considere o seguinte:
<xsl:variable name="x">
<content />
<content />
</xsl:variable>
onde $x
variável contém uma sequência de um nó: o nó pai de content
. Aqui, count($x)
sempre lhe dará 1
. Para obter a quantidade de elementos content
, você precisa para contar os filhos do nó raiz implícita $x
:. count($x/content)
Como uma regra de ouro, você pode se lembrar que: se xsl:variable
é em si um elemento vazio com um atributo select
, o conjunto de resultados será atribuído à variável. Se você usar xsl:variable
sem o atributo select
, você sempre criar um pai implícita com a variável, e o conteúdo da variável são os filhos debaixo dela .
O mesmo se aplica para o seu exemplo, com xsl:sequence
como uma criança a xsl:variable
. Por ter um xsl:variable
não vazio você cria um pai implícita para a seqüência, que é por isso que você sempre terá 1
se você contar a própria variável.
Se você precisa de ambos: um conjunto de declarações dentro xsl:variable
, mas o comportamento do atributo select
, você pode resolver isso usando o seguinte:
<xsl:variable name="x" select="$y/*" />
<xsl:variable name="y">
<xsl:sequence select="foo" />
</xsl:variable>
que passará a produzir a quantidade esperada para count($x)
, mas se ou não isso é realmente benéficos ou torna seu código mais claro é discutível.
Outras dicas
Ele funciona como você poderia esperar se você quer mudar a declaração de variável para:
<xsl:variable name="seq" select="*"/>
ou declarar o tipo da variável usando 'como' atributo:
<xsl:variable name="seq" as="item()*">
<xsl:sequence select="*" />
</xsl:variable>
Não especificando qualquer informação do tipo frequentemente Leeds para resultados surpreendentes em XSLT 2.0. Se você estiver usando Saxon, você pode saída como Saxon interpreta a folha de estilo usando a explicar atributo de extensão:
<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>
Como você pode ver, Saxon constrói um documento de nó fora da seqüência:
Optimized expression tree for template at line 6 in :
let $seq[refCount=1] as document-node() :=
document-constructor
child::element()
return
message
Você está selecionando os nós sub, e, em seguida, contando cada nó - por isso vai ser sempre 1. Você precisa contar as crianças, por exemplo:
<xsl:value-of select="count($seq/*)" />
lhe dará o resultado que você está esperando.