Domanda

Ho un documento XML e desidero modificare i valori per uno degli attributi.

Per prima cosa ho copiato tutto dall'input all'output usando:

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

E ora voglio cambiare il valore dell'attributo " type " in qualsiasi elemento chiamato " property " .

È stato utile?

Soluzione

Testato su un semplice esempio, funziona benissimo:

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

Modificato per includere il suggerimento di Tomalak.

Altri suggerimenti

Questo problema ha una soluzione classica : Utilizzo e sostituzione il modello di identità è uno dei modelli di progettazione XSLT più fondamentali e potenti :

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

Se applicato su questo documento XML :

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

viene prodotto il risultato desiderato :

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

Le prime due risposte non funzioneranno se è presente una definizione xmlns nell'elemento root:

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

Tutte le soluzioni non funzioneranno per il precedente XML.

La possibile soluzione è come:

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

È necessario un modello che corrisponda all'attributo target e nient'altro.

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

Questo è in aggiunta a " copia tutto " hai già (ed è effettivamente sempre presente di default in XSLT). Avendo una corrispondenza più specifica verrà utilizzata in preferenza.

Ho avuto un caso simile in cui volevo eliminare un attributo da un nodo semplice e non riuscivo a capire quale asse mi avrebbe permesso di leggere il nome dell'attributo. Alla fine, tutto quello che dovevo fare era usare

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

Per il seguente XML:

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

Sono stato in grado di farlo funzionare con la seguente XSLT:

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

Se il documento XML di origine ha un proprio spazio dei nomi, è necessario dichiarare lo spazio dei nomi nel foglio di stile, assegnargli un prefisso e utilizzare tale prefisso quando si fa riferimento agli elementi dell'XML di origine, ad esempio:

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

Oppure, se preferisci:

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

APPENDICE: Nel caso altamente improbabile in cui lo spazio dei nomi XML non sia noto in precedenza, è possibile eseguire:

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

Ovviamente, è molto difficile immaginare uno scenario in cui si saprebbe in anticipo che il documento XML di origine contiene un elemento chiamato " proprietà " ;, con un attributo chiamato " tipo " che deve essere sostituito, ma non conosce ancora lo spazio dei nomi del documento. Ho aggiunto questo principalmente per mostrare come la tua soluzione potrebbe essere semplificata.

Ho anche riscontrato lo stesso problema e l'ho risolto come segue:

<!-- 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>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top