Annulla il pivot del documento XML in base agli attributi
Domanda
Ho un semplice documento XML che assomiglia al seguente frammento.Devo scrivere una trasformazione XSLT che sostanzialmente "sblocchi" questo documento in base ad alcuni attributi.
<?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>
Questo è quello che mi aspetto che sia l'output:
<?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>
Apprezzo il tuo aiuto.
Soluzione
<xsl:template match="row">
<row A="{$A}" X="{$X}" />
<row A="{$A}" Y="{$Y}" />
<row A="{$A}" Z="{$Z}" />
</xsl:template>
Inoltre, ovvio standard.
Altri suggerimenti
Ecco il foglio di stile completo di cui hai bisogno (poiché gli spazi dei nomi sono importanti):
<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>
Preferisco di gran lunga utilizzare elementi di risultato letterali (ad es <z:row>
) piuttosto che <xsl:element>
e modelli di valori di attributo (quelli {}
s nei valori degli attributi) anziché <xsl:attribute>
ove possibile in quanto rende il codice più breve e rende più semplice vedere la struttura del documento risultato che stai generando.Altri preferiscono <xsl:element>
E <xsl:attribute>
perché allora tutto è un'istruzione XSLT.
Se stai utilizzando XSLT 2.0, ci sono un paio di sottigliezze sintattiche che aiutano, vale a dire il file except
operatore in XPath e la possibilità di utilizzare a select
attribuire direttamente su <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>
Questo è più complesso ma anche più generico:
<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>
Ecco un metodo un po' di forza 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>