Pergunta

Estou tentando fazer uma transformação em um documento XML. Minha transformação XML pode resultar em dois tipos diferentes de elemento base, dependendo do valor de um determinado elemento:

<xsl:template match="/">
  <xsl:choose>
    <xsl:when test="/databean/data[@id='pkhfeed']/value/text()='200'">
      <xsl:call-template name="StructureA">
        <xsl:with-param name="structure" select="//databean" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="StructureB">
        <xsl:with-param name="structure" select="//databean" />
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Estruturea ou estruturab são então criados com seus próprios namespaces e esquemalocações:

<StructureA xmlns="http://...">

A Strucurea & B compartilham alguns elementos comuns, portanto, são definidos em um arquivo separado chamado "xmlcommon.xslt", dos quais ambas as estruturas incluem modelos. Este arquivo XMLCommon não possui um espaço para nome padrão definido, pois eu quero que ele seja utilizável no espaço para nome definido em Structurea ou StructionB. Mas quando eu corro minha transformação, quaisquer modelos retirados do arquivo comum resultam em atributos em branco XMLNS:

<StructureA xmlns="http://...">
  <SharedElement xmlns="">Something</SharedElement>
</StructureA>

Ao validar, o espaço para nome em branco é usado em vez do pai correto. Alguém sabe como posso parar meus modelos no meu arquivo comum de adicionar esses atributos em branco XMLNs?

Aqui está um trecho do arquivo comum:

<xsl:stylesheet version="1.0" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template name="ControlledListStructure">
    <xsl:param name="xmlElem" />
    <xsl:param name="structure" />

    <xsl:element name="{$xmlElem}">
      <!-- Blah blah blah -->
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>
Foi útil?

Solução

A principal coisa a perceber é que sua folha de estilo determina o nome de cada elemento que você adiciona à árvore de resultados. O nome de um elemento tem duas partes: o nome local e o espaço de nome URI. No seu código acima, você fornece o nome local (o valor de $xmlElem), mas você não especifica um URI de namespace, o que significa que ele será inadimplente para a string vazia. (Na verdade, ele assume o espaço de nome padrão desse módulo de folha de estilo; como não há nenhum, então é a sequência vazia.) Em outras palavras, o elemento será não em um espaço para nome. Ao serializar o documento, o processador XSLT deve incluir o xmlns="" não-declarações para não declarar o espaço para nome padrão que aparece na parte superior. Caso contrário, o elemento assumiria esse espaço para nome, que não é o que sua folha de estilo dita. A maneira menos intrusiva de consertar isso seria adicionar outro parâmetro (por exemplo $namespaceURI), assim como você tem com $xmlElem. Então você escreveria:

<xsl:element name="{$xmlElem}" namespace="{$namespaceURI}">

Agora, o elemento resultante assumirá o espaço para nome que você pedir para assumir (o que terá o efeito de remover esses namespace padrão sem declarações).

Isso deve responder a sua pergunta. Eu ofereço o seguinte como material bônus gratuito. ;-)

Você deve remover o text() Teste de nó em sua comparação de valor. Muito raramente você precisará comparar diretamente os valores dos nós de texto. Em vez disso, você pode apenas comparar o valor da string do próprio elemento (que é definido como a concatenação dos valores de string de todos os seus nós de texto descendente). Isso seria assim:

<xsl:when test="/databean/data[@id='pkhfeed']/value = '200'">

A vantagem de fazê -lo dessa maneira é que seu código não quebre se houver um comentário escondido lá:

<value>2<!--test-->00</value>

Nesse caso, existem dois nós de texto ("2" e "00"). Seu teste original falharia, pois ele verifica se algum deles é igual a "200". Não é muito provável que ocorra neste caso, mas, em qualquer caso, testar o valor da string do elemento (em oposição às crianças do nó de texto) é uma boa prática quando essa é a sua intenção.

Por fim, encorajo você a aprender sobre regras de modelo e contexto XPath. Eu tendem a evitar <xsl:choose>, <xsl:call-template>, e <xsl:with-param> quando possível. Por um lado, as regras de modelo podem ajudar a evitar muitas partes feias e detalhadas do XSLT.

<xsl:template match="/databean[data[@id='pkhfeed']/value = '200']" priority="1">
  <StructureA xmlns="http://...">
    ...
  </StructureA>
</xsl:template>

<xsl:template match="/databean">
  <StructureB xmlns="http://...">
    ...
  </StructureB>
</xsl:template>

Mesmo se você continuar usando <xsl:call-template>, você não deveria ter que passar por isso $structure O parâmetro, como o nó atual permanecerá inalterado no modelo chamado. Você pode acessar //databean (ou /databean, que eu suspeito que é o que você quer dizer) da mesma forma que dentro de qualquer um de seus StructureA ou StructureB Modelos, porque o nó atual ainda será "/" (o nó do documento).

Se você estiver interessado em aprender mais sobre o modelo de processamento principal do XSLT e seus recursos mais poderosos (regras de modelo), encorajo você a conferir "Como o XSLT funciona", o capítulo de amostra grátis do meu Referência de bolso XSLT 1.0.

Espero que isso tenha sido útil para você, mesmo que seja mais do que você negociou!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top