Pregunta

Estoy intentando hacer una transformación en un documento XML. Mi transformación XML puede dar como resultado dos tipos diferentes de elementos base dependiendo del valor de un elemento determinado:

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

StructureA o StructureB se crean con sus propios espacios de nombres y schemaLocations:

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

EstructuraA & amp; B comparte algunos elementos comunes, por lo que estos se definen en un archivo separado llamado " xmlcommon.xslt " que ambas estructuras incluyen plantillas de. Este archivo xmlcommon no tiene un espacio de nombres predeterminado definido, ya que quiero que sea utilizable desde el espacio de nombres definido en StructureA o StructureB. Pero cuando ejecuto mi transformación, todas las plantillas extraídas del archivo común dan como resultado atributos xmlns en blanco:

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

Al validar, el espacio de nombres en blanco se usa en lugar del principal correcto. ¿Alguien sabe cómo puedo evitar que mis plantillas en mi archivo común agreguen esos atributos xmlns en blanco?

Aquí hay un fragmento del archivo común:

<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>
¿Fue útil?

Solución

Lo importante es darse cuenta de que su hoja de estilo dicta el nombre de cada elemento que agrega al árbol de resultados. El nombre de un elemento tiene dos partes: el nombre local y el URI del espacio de nombres. En su código anterior, proporciona el nombre local (el valor de $ xmlElem ), pero no especifica un URI de espacio de nombres, lo que significa que se establecerá de forma predeterminada en la cadena vacía. (En realidad, toma el espacio de nombres predeterminado de ese módulo de hojas de estilo; dado que no hay ninguno, entonces es la cadena vacía). En otras palabras, el elemento será no en un espacio de nombres . Al serializar el documento, el procesador XSLT debe incluir las declaraciones no declaradas xmlns = " " para no declarar el espacio de nombres predeterminado que aparece en la parte superior. De lo contrario, el elemento tomaría ese espacio de nombres, que no es lo que dictó su hoja de estilos. La forma menos intrusiva de arreglar esto sería agregar otro parámetro (por ejemplo, $ namespaceURI ), tal como lo hizo con $ xmlElem . Luego escribirías:

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

Ahora, el elemento resultante tomará cualquier espacio de nombres que le digas que tome (lo que tendrá el efecto de eliminar esas no declaraciones de espacios de nombres predeterminadas).

Eso debería responder a tu pregunta. Ofrezco lo siguiente como material extra gratis. ;-)

Debe eliminar la prueba de nodo text () en su comparación de valores. Muy raramente necesitará comparar directamente los valores de los nodos de texto. En su lugar, solo puede comparar el valor de cadena del elemento en sí (que se define como la concatenación de los valores de cadena de todos sus nodos de texto descendientes). Eso se vería así:

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

La ventaja de hacerlo de esta manera es que tu código no se romperá si hay un comentario escondido allí:

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

En este caso, hay dos nodos de texto (" 2 " y " 00 "). Su prueba original fallaría, ya que verifica si alguno de ellos es igual a " 200 " No es muy probable que ocurra en este caso, pero en cualquier caso, probar el valor de cadena del elemento (a diferencia de sus hijos de nodo de texto) es una buena práctica cuando esa es su intención.

Finalmente, te animo a que aprendas sobre las reglas de la plantilla y el contexto XPath. Tiendo a evitar < xsl: elegir > , < xsl: call-template > , y < xsl: with-param > cuando sea posible. Por un lado, las reglas de plantilla pueden ayudarlo a evitar muchas de las partes feas y detalladas de 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>

Incluso si sigue utilizando < xsl: call-template > , no debería tener que pasar ese parámetro $ structure , ya que el nodo actual permanecerá sin cambios. En la plantilla llamada. Puede acceder a // databean (o / databean , que sospecho que es lo que quiere decir) con la misma facilidad desde cualquiera de sus StructureA o StructureB , porque el nodo actual seguirá siendo " / " (el nodo del documento).

Si está interesado en obtener más información sobre el modelo de procesamiento central de XSLT y sus funciones más poderosas (reglas de plantilla), lo invito a visitar " Cómo funciona XSLT " , el capítulo de muestra gratuita de mi XSLT 1.0 Pocket Reference .

¡Espero que esto te haya sido útil, incluso si es más de lo que esperabas!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top