/ / XSLT transformation en attibute - xml, xslt

Transformation XSLT vers l'attibute - xml, xslt

J'ai la question suivante concernant XSLTtransformer - J'ai besoin de transformer le fichier XML en tenant compte des valeurs d'attribut dans tous les nœuds parents jusqu'à la racine. Donc, avoir un code comme celui-ci (considérez l'attribut XY comme une chaîne avec les valeurs "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>

J'ai besoin d'une sortie

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

J'essayais d'utiliser une transformation XSLT en utilisant un modèle comme celui-ci:

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

mais ceci n'est une solution que pour un niveau supérieur. Est-il possible de faire une telle transformation uniquement avec l'utilisation de XSLT?

Réponses:

1 pour la réponse № 1

Si vous travaillez avec XSLT 1.0, vous avez besoin d’un modèle qui parcourt l’arborescence de noeuds de manière récursive:

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

Lorsqu'il est exécuté sur votre exemple XML, le résultat est le suivant:

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

Si vous avez un processeur XSLT 2.0 disponible, la solution est beaucoup plus simple:

<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 pour la réponse № 2

Je le ferais de cette façon:

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 pour la réponse № 3

Il existe un moyen d'y parvenir dans XSLT 1.0 sans gabarit récursif (pas qu'il y ait un problème avec ça!) Et c'est pour passer le total cumulé gabarit à gabarit en paramètre. Essentiellement, utilisez le newOffsetX paramètre dans le modèle d'identité aussi.

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

La drôle d'expression substring-before(concat("0", @XY, ","), ",") est de faire face à des nœuds sans @XY attribut. Quand un @XY L'attribut est présent, avec une valeur de "40,20" par exemple, le "concat" renvoie "040,20" et le nombre X est donc toujours le même. Quand le @XY n'est pas présent, le concat résultats 0, et donc le nombre X est "0".