web-dev-qa-db-fra.com

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

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

D'abord, j'ai tout copié de l'entrée à 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 n'importe quel élément nommé "property".

55
tomato

Testé sur un exemple simple, 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>

Édité pour inclure la suggestion de Tomalak.

36
Welbog

Ce problème a une solution classique: Utiliser et écraser le 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 recherché est produit:

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

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

<?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 comme:

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

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

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

Cela s'ajoute à la "copie complète" que vous avez déjà (et est toujours présent par défaut dans XSLT). Ayant une correspondance plus spécifique, il sera utilisé de préférence.

4
Richard

Dans un cas similaire, je voulais supprimer un attribut d'un nœud simple et je ne pouvais pas savoir quel axe me permettait de lire le nom de l'attribut. À la fin, tout ce que je devais faire était d’utiliser

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

2
rwrobson

Pour le XML suivant:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <property type="foo"/>
    <node id="1"/>
    <property type="bar">
        <sub-property/>
    </property>
</root>

J'ai pu le faire fonctionner avec le XSLT suivant:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="//property">
        <xsl:copy>
            <xsl:attribute name="type">
                <xsl:value-of select="@type"/>
                <xsl:text>-added</xsl:text>
            </xsl:attribute>
            <xsl:copy-of select="child::*"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
1
Andrew Hare

Si votre document XML source possède son propre espace de noms, vous devez le déclarer dans votre feuille de style, lui attribuer un préfixe et utiliser ce préfixe lorsque vous vous référez aux éléments du code XML source, par exemple:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml">

<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />

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

<!-- exception-->    
<xsl:template match="xhtml:property/@type">
    <xsl:attribute name="type">
        <xsl:text>some new value</xsl:text> 
    </xsl:attribute>
</xsl:template>

</xsl:stylesheet>

Ou, si vous préférez:

...
<!-- exception-->    
<xsl:template match="@type[parent::xhtml:property]">
  <xsl:attribute name="type">
        <xsl:text>some new value</xsl:text> 
  </xsl:attribute>
</xsl:template>
...

ADDENDUM: Dans le cas très improbable où l'espace de nom XML n'est pas connu à l'avance, vous pouvez effectuer les opérations suivantes:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />

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

<!-- exception -->
<xsl:template match="*[local-name()='property']/@type">
    <xsl:attribute name="type">
        <xsl:text>some new value</xsl:text> 
    </xsl:attribute>
</xsl:template>

Bien entendu, il est très difficile d’imaginer un scénario dans lequel vous sauriez à l’avance que le document XML source contient un élément nommé "propriété", avec un attribut nommé "type" qui doit être remplacé - sans pour autant connaître l’espace de nom du document. J'ai ajouté ceci principalement pour montrer comment votre propre solution pourrait être rationalisée.

1
michael.hor257k

J'ai également rencontré le même problème et je l'ai résolu comme suit:

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

<!-- copy property element while only changing its type attribute -->
<xsl:template match="property">
  <xsl:copy>
    <xsl:attribute name="type">
      <xsl:value-of select="'your value here'"/>
    </xsl:attribute>
    <xsl:apply-templates select="@*[not(local-name()='type')]|node()"/>
  <xsl:copy>
</xsl:template>
0
Ashish Lahoti