67 votes

XSLT : Comment changer la valeur d'un attribut pendant <xsl:copy> ?

J'ai un document XML et je veux modifier les valeurs de l'un des attributs.

J'ai d'abord copié tout ce qui se trouve en entrée vers la sortie en utilisant :

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

Et maintenant je veux changer la valeur de l'attribut "type" dans tout élément nommé "property" .

1 votes

Pour ceux qui veulent une solution générale : <xsl:stylesheet xmlns:xsl=" w3.org/1999/XSL/Transformer " version="1. 0"> <xsl:template match="node()[local-name()='property']/@*[local-name()='type']"> <xsl:attribute name="{name()}" namespace="{namespace-uri()}">une nouvelle valeur ici </xsl:attribute> <xsl:template> <xsl : template match="@*|node()|comment()|processing-instruction()|text()"> <xsl:copy> <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>

2 votes

Votre solution est inutilement verbeuse, et partiellement fausse. Il devrait y avoir ' http://www. au début de la page xsl espace de noms. De même, la correspondance/sélection de node()|comment()|processing-instruction()|text() est superflu, puisque les commentaires, les instructions de traitement et les nœuds de texte sont appariés par node() .

0 votes

@Flynn1179 Ma solution fonctionne bien pour toutes les situations. Je ne sais pas pourquoi http:// est manquant après le copier/coller, c'est une erreur, merci de l'avoir signalé. J'ai juste donné une solution possible, pas la solution parfaite. La chose la plus importante est que ma solution fonctionne pour presque toutes les situations bien que "ce soit superflu" comme vous l'avez dit. D'un autre côté, la plupart des autres réponses, y compris celle de "l'expert xslt", ne fonctionnent pas du tout. Mais ils ne l'ont pas admis.

65voto

Dimitre Novatchev Points 147842

Ce problème a une solution classique : Utilisation et remplacement le site modèle d'identité est l'un des modèles de conception XSLT les plus fondamentaux et les plus puissants. :

<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:param name="pNewType" select="'myNewType'"/>

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="property/@type">
        <xsl:attribute name="type">
            <xsl:value-of select="$pNewType"/>
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

Lorsqu'il est appliqué à ce document XML :

<t>
  <property>value1</property>
  <property type="old">value2</property>
</t>

le résultat souhaité est produit :

<t>
  <property>value1</property>
  <property type="myNewType">value2</property>
</t>

1 votes

Cette solution ne fonctionne pas s'il existe une définition d'espace de nom. J'ai écrit un commentaire il y a quelques jours, et l'auteur de la réponse m'a répondu. Mais ils sont partis maintenant, alors je dois reposter le commentaire à ceux qui viennent ici pour ne pas être induits en erreur par ces mauvaises réponses, surtout par ces auteurs qui ont eu tendance à être mal orientés.

0 votes

Peut-être que vous vous concentrez trop sur la théorie au lieu du problème lui-même. Google m'a conduit ici, votre réponse est utile, mais ne peut pas du tout résoudre mon problème. J'ai donc fini par en trouver une meilleure, qu'elle soit théoriquement bonne ou mauvaise, ou qu'elle puisse rendre quelqu'un fou des espaces de noms. Ce qui m'importe, c'est de trouver un moyen de résoudre mon problème et j'espère que mon expérience pourra aider d'autres personnes qui se trouvent dans des situations similaires. Votre réponse est vraiment utile, et vous êtes vraiment un répondant enthousiaste ici. Mais je dois dire que la solution que vous avez donnée pour cette question ne fonctionne pas du tout.

0 votes

Cette solution ne fonctionne pas non plus s'il y a une définition d'espace de nom sur l'élément Root.

41voto

Welbog Points 32952

Testé sur un exemple simple, il fonctionne bien :

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="@type[parent::property]">
  <xsl:attribute name="type">
    <xsl:value-of select="'your value here'"/>
  </xsl:attribute>
</xsl:template>

Modifié pour inclure la suggestion de Tomalak.

1 votes

Une version alternative serait <xsl:template match="@type[parent::property]">.

0 votes

Je suis d'accord. Votre méthode est probablement plus intuitive, car elle correspond plus logiquement à l'objectif du modèle.

1 votes

C'est aussi ce que je voulais dire dans le commentaire original, mais j'ai oublié de le taper ;-)

12voto

astonia Points 140

Les deux premières réponses ne fonctionneront pas s'il y a une définition xmlns dans l'élément Root :

<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
    <property type="old"/>
</html>

Toutes les solutions ne fonctionneront pas pour le xml ci-dessus.

La solution possible est la suivante :

<?xml version="1.0"?> 

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:template match="node()[local-name()='property']/@*[local-name()='type']">
      <xsl:attribute name="{name()}" namespace="{namespace-uri()}">
                some new value here
          </xsl:attribute>
  </xsl:template>

  <xsl:template match="@*|node()|comment()|processing-instruction()|text()">
      <xsl:copy>
          <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()"/>
      </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

0 votes

Vous rendez ça beaucoup plus compliqué que ça ne doit l'être. J'ai publié une réponse qui montre comment faire fonctionner les deux premières réponses dans votre situation.

0 votes

Votre réponse est beaucoup plus compliquée que la mienne. Je ne vois pas pourquoi vous donnez une réponse supplémentaire après mon message. Ce que vous devriez faire, c'est ajouter ma réponse. Et franchement, votre réponse est fausse si l'attribut a aussi un espace de nom.

5voto

Richard Points 54016

Vous avez besoin d'un modèle qui correspondra à votre attribut cible, et rien d'autre.

<xsl:template match='XPath/@myAttr'>
  <xsl:attribute name='myAttr'>This is the value</xsl:attribute>
</xsl:template>

Ceci s'ajoute à la fonction "copy all" que vous avez déjà (et qui est en fait toujours présente par défaut dans XSLT). Ayant une correspondance plus spécifique, elle sera utilisée de préférence.

0 votes

J'ai essayé sans la partie "copy all" et je n'ai obtenu que ce qui se trouvait entre les balises. Aucune des balises elles-mêmes ou des attributs n'a été copiée.

0 votes

+1 à cause de sa simplicité et parce que cela fonctionnera à la fois pour le cas d'utilisation présenté, et pour des xpaths beaucoup plus complexes où vous voulez seulement changer l'attribut d'un élément à un xpath très spécifique (ce qui est ce que je cherchais quand je suis venu sur cette page).

2voto

rwrobson Points 31

J'ai eu un cas similaire où je voulais supprimer un attribut d'un simple nœud, et je n'arrivais pas à trouver l'axe qui me permettrait de lire le nom de l'attribut. Finalement, tout ce que j'avais à faire était d'utiliser

@*[name(.)!='AttributeNameToDelete']

1 votes

+1 car cette construction est utile si on veut changer un attribut dans une copie. mais la réponse est incomplète. Voir cette réponse pour savoir ce que je veux dire : stackoverflow.com/a/12919373/520567

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X