Ho la seguente domanda su XSLTTrasforma: devo trasformare il file XML considerando i valori degli attributi in tutti i nodi principali fino alla radice. Quindi avere un codice come questo (considera l'attributo XY come una stringa con i valori "X, Y"):
<Layout XY="40,20">
<Layout XY="0,20">
<Circle OffsetX="0"/>
</Layout>
<Circle OffsetX="6" />
<Layout XY="100,20">
<Circle OffsetX="0"/>
<Layout XY="200,20">
<Circle OffsetX="5"/>
</Layout>
</Layout>
</Layout>
Ho bisogno di un risultato
<Layout XY="40,20">
<Layout XY="0,20">
<Circle OffsetX="40"/>
</Layout>
<Circle OffsetX="46" />
<Layout XY="100,20">
<Circle OffsetX="140"/>
<Layout XY="200,20">
<Circle OffsetX="345"/>
</Layout>
</Layout>
</Layout>
Stavo cercando di usare una trasformazione XSLT usando un modello come questo:
<!-- Copy template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Change values -->
<xsl:template match="Circle/@OffsetX">
<xsl:param name="newOffsetX" select="substring-before(../../@XY,",")"/>
<xsl:apply-templates select="@*|node()"/>
<xsl:attribute name="OffsetX">
<xsl:value-of select=".+$newOffsetX"/>
</xsl:attribute>
</xsl:template>
ma questa è una soluzione solo per un livello superiore. È possibile fare una simile trasformazione solo con l'uso di XSLT?
risposte:
1 per risposta № 1Se stai lavorando con XSLT 1.0, hai bisogno di un modello che attraversi ricorsivamente l'albero dei nodi:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@XY" mode="sumXOffset" name="SumXOffset">
<xsl:param name="currentValue"
select="substring-before(., ",")" />
<xsl:variable name="sum">
<xsl:apply-templates select="(../ancestor::*/@XY)[last()]" mode="sumXOffset" />
</xsl:variable>
<xsl:value-of select="$currentValue +
concat("0", $sum)" />
</xsl:template>
<xsl:template match="@OffsetX">
<xsl:attribute name="{name()}">
<xsl:call-template name="SumXOffset">
<xsl:with-param name="currentValue" select="." />
</xsl:call-template>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Quando eseguito sull'XML di esempio, il risultato è:
<Layout XY="40,20">
<Layout XY="0,20">
<Circle OffsetX="40" />
</Layout>
<Circle OffsetX="46" />
<Layout XY="100,20">
<Circle OffsetX="140" />
<Layout XY="200,20">
<Circle OffsetX="345" />
</Layout>
</Layout>
</Layout>
Se hai un processore XSLT 2.0 disponibile, la soluzione è molto più semplice:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@OffsetX">
<xsl:attribute name="{name()}">
<xsl:value-of select=". + sum(ancestor::*/@XY/substring-before(","))"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
1 per risposta № 2
Lo farei in questo modo:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Circle/@OffsetX">
<xsl:variable name="previous-X">
<xsl:for-each select="ancestor::*[@XY]">
<x><xsl:value-of select="substring-before(@XY, ",")"/></x>
</xsl:for-each>
</xsl:variable>
<xsl:attribute name="OffsetX">
<xsl:value-of select=". + sum(exsl:node-set($previous-X)/x)"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
0 per risposta № 3
C'è un modo per raggiungere questo obiettivo in XSLT 1.0 senza un modello ricorsivo (non che ci sia qualcosa di sbagliato in questo!) E cioè passare il totale parziale da modello a modello come parametro. In sostanza, utilizzare il newOffsetX
parametro anche nel modello di identità.
Prova questo XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:param name="newOffsetX" select="0" />
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:with-param name="newOffsetX" select="$newOffsetX + number(substring-before(concat("0", @XY, ","), ","))" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="@OffsetX">
<xsl:param name="newOffsetX" select="0" />
<xsl:attribute name="OffsetX">
<xsl:value-of select="$newOffsetX + number(.)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
L'espressione divertente substring-before(concat("0", @XY, ","), ",")
è far fronte a nodi con no @XY
attributo. Quando un @XY
L'attributo è presente, ad esempio con un valore di "40,20", il "concat" restituisce "040,20" e quindi il numero X è sempre numericamente uguale. Quando il @XY
non è presente, il concat
ritorna 0,
e quindi il numero X è "0".