Pregunta

Tengo un documento XML y quiero cambiar los valores de uno de los atributos.

Primero copié todo desde la entrada hasta la salida usando:

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

Y ahora quiero cambiar el valor del atributo " tipo " en cualquier elemento llamado " propiedad " .

¿Fue útil?

Solución

Probado en un ejemplo simple, funciona 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>

Editado para incluir la sugerencia de Tomalak.

Otros consejos

Este problema tiene una solución clásica : Uso y reemplazo la plantilla de identidad es uno de los patrones de diseño XSLT más fundamentales y poderosos :

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

Cuando se aplica en este documento XML :

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

se produce el resultado deseado :

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

Las dos respuestas principales no funcionarán si hay una definición xmlns en el elemento raíz:

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

Todas las soluciones no funcionarán para el xml anterior.

La posible solución es como:

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

Necesita una plantilla que coincida con su atributo de destino, y nada más.

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

Esto es además de la " copiar todos " ya tiene (y en realidad siempre está presente de forma predeterminada en XSLT). Al tener una coincidencia más específica, se utilizará con preferencia.

Tuve un caso similar en el que quería eliminar un atributo de un nodo simple, y no pude averiguar qué eje me permitiría leer el nombre del atributo. Al final, todo lo que tenía que hacer era usar

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

Para el siguiente XML:

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

Pude hacerlo funcionar con el siguiente 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>

Si su documento XML de origen tiene su propio espacio de nombres, debe declarar el espacio de nombres en su hoja de estilo, asignarle un prefijo y usar ese prefijo cuando haga referencia a los elementos del XML de origen, por ejemplo:

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

O, si prefieres:

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

ADENDA: En el caso altamente improbable de que el espacio de nombres XML no se conozca de antemano, podría hacerlo:

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

Por supuesto, es muy difícil imaginar un escenario en el que sabría de antemano que el documento XML de origen contiene un elemento denominado "propiedad", con un atributo llamado "tipo". Eso necesita ser reemplazado, pero aún no se conoce el espacio de nombres del documento. He agregado esto principalmente para mostrar cómo se podría simplificar su propia solución.

También encontré el mismo problema y lo resolví de la siguiente manera:

<!-- 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>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top