Desvincular documento xml basado en atributos
Pregunta
Tengo un documento xml simple que se parece al siguiente fragmento.Necesito escribir una transformación XSLT que básicamente "desvincule" este documento en función de algunos de los atributos.
<?xml version="1.0" encoding="utf-8" ?>
<root xmlns:z="foo">
<z:row A="1" X="2" Y="n1" Z="500"/>
<z:row A="2" X="5" Y="n2" Z="1500"/>
</root>
Esto es lo que espero que sea el resultado:
<?xml version="1.0" encoding="utf-8" ?>
<root xmlns:z="foo">
<z:row A="1" X="2" />
<z:row A="1" Y="n1" />
<z:row A="1" Z="500"/>
<z:row A="2" X="5" />
<z:row A="2" Y="n2"/>
<z:row A="2" Z="1500"/>
</root>
Aprecio tu ayuda.
Solución
<xsl:template match="row">
<row A="{$A}" X="{$X}" />
<row A="{$A}" Y="{$Y}" />
<row A="{$A}" Z="{$Z}" />
</xsl:template>
Además de un texto repetitivo obvio.
Otros consejos
Aquí está la hoja de estilo completa que necesita (ya que los espacios de nombres son importantes):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:z="foo">
<xsl:template match="root">
<root>
<xsl:apply-templates />
</root>
</xsl:template>
<xsl:template match="z:row">
<xsl:variable name="A" select="@A" />
<xsl:for-each select="@*[local-name() != 'A']">
<z:row A="{$A}">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</z:row>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Prefiero usar elementos de resultado literales (p. ej. <z:row>
) en vez de <xsl:element>
y plantillas de valor de atributo (aquellas {}
s en valores de atributos) en lugar de <xsl:attribute>
siempre que sea posible, ya que acorta el código y facilita ver la estructura del documento de resultados que está generando.Otros prefieren <xsl:element>
y <xsl:attribute>
porque entonces todo es una instrucción XSLT.
Si está utilizando XSLT 2.0, hay un par de sutilezas sintácticas que ayudan, a saber, el except
operador en XPath y la capacidad de utilizar un select
atributo directamente en <xsl:attribute>
:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
xmlns:z="foo">
<xsl:template match="root">
<root>
<xsl:apply-templates />
</root>
</xsl:template>
<xsl:template match="z:row">
<xsl:variable name="A" as="xs:string" select="@A" />
<xsl:for-each select="@* except @A">
<z:row A="{$A}">
<xsl:attribute name="{local-name()}" select="." />
</z:row>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Esto es más complejo pero también más genérico:
<xsl:template match="z:row">
<xsl:variable name="attr" select="@A"/>
<xsl:for-each select="@*[(local-name() != 'A')]">
<xsl:element name="z:row">
<xsl:copy-of select="$attr"/>
<xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:template>
Aquí hay una forma un poco de fuerza bruta:
<xsl:template match="z:row">
<xsl:element name="z:row">
<xsl:attribute name="A">
<xsl:value-of select="@A"/>
</xsl:attribute>
<xsl:attribute name="X">
<xsl:value-of select="@X"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="z:row">
<xsl:attribute name="A">
<xsl:value-of select="@A"/>
</xsl:attribute>
<xsl:attribute name="Y">
<xsl:value-of select="@Y"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="z:row">
<xsl:attribute name="A">
<xsl:value-of select="@A"/>
</xsl:attribute>
<xsl:attribute name="Z">
<xsl:value-of select="@Z"/>
</xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>