Pergunta

Eu tenho um documento XML e quero alterar os valores para um dos atributos.

Primeiro, copiei tudo, desde a entrada até a saída usando:

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

E agora quero mudar o valor do atributo "type" em qualquer elemento nomeado "property".

Foi útil?

Solução

Testado em um exemplo simples, funciona bem:

<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 a sugestão de Tomalak.

Outras dicas

Este problema tem uma solução clássica: Usando e substituindo a modelo de identidade é um dos padrões de design XSLT mais fundamentais e 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>

Quando aplicado neste documento XML:

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

O resultado desejado é produzido:

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

As duas principais respostas não funcionarão se houver uma definição XMLNS no elemento raiz:

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

Todas as soluções não funcionarão para o XML acima.

A solução possível é 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>

Você precisa de um modelo que corresponda ao seu atributo de destino e nada mais.

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

Isso é um acréscimo ao "copiar tudo" que você já possui (e na verdade está sempre presente por padrão no XSLT). Tendo uma correspondência mais específica, será usado em preferência.

Eu tinha um caso semelhante em que queria excluir um atributo de um nó simples e não conseguia descobrir qual eixo me deixaria ler o nome do atributo. No final, tudo que eu tinha que fazer era usar

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

Para o seguinte XML:

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

Consegui fazê -lo funcionar com o seguinte 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 o seu documento XML de origem tiver seu próprio espaço para nome, você precisará declarar o espaço para nome da sua folha de estilo, atribuir um prefixo e usar esse prefixo ao se referir aos elementos do XML de origem - por exemplo:

<?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, se você preferir:

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

Adendo: no caso altamente improvável em que o espaço para nome XML não é conhecido de antemão, você pode fazer:

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

Obviamente, é muito difícil imaginar um cenário em que você saberia com antecedência que o documento XML da fonte contém um elemento chamado "Propriedade", com um atributo chamado "Type" que precisa ser substituído - mas ainda não conhece o espaço de nome do documento. Eu adicionei isso principalmente para mostrar como sua própria solução poderia ser simplificada.

Também me deparei com a mesma edição e resolvi da seguinte maneira:

<!-- 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top