web-dev-qa-db-ja.com

XSLT:<xsl:copy>中に属性値を変更する方法

XML文書があり、属性の1つの値を変更したい。

最初に、入力から出力まですべてをコピーしました:

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

次に、"type"という名前の要素の"property"属性の値を変更します。

56
tomato

簡単な例でテストし、正常に動作します:

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

トマラックの提案を含むように編集されました。

36
Welbog

この問題には古典的な解決策があります使用およびオーバーライド identity template は最も基本的で強力なXSLT設計パターン

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

このXMLドキュメントに適用される場合

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

必要な結果が生成されます

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

ルート要素にxmlns定義がある場合、上位2つの回答は機能しません。

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

すべてのソリューションは、上記のxmlでは機能しません。

考えられる解決策は次のとおりです。

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

ターゲット属性に一致するテンプレートのみが必要です。

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

これは、すでに持っている「すべてコピー」に追加されます(実際、XSLTにはデフォルトで常に存在しています)。より具体的に一致するものが優先的に使用されます。

4
Richard

単純なノードから1つの属性を削除したいという似たようなケースがありましたが、どの軸が属性名を読み取ることができるかわかりませんでした。最後に、私がしなければならなかったのは

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

2
rwrobson

次のXMLの場合:

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

次の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>
1
Andrew Hare

ソースXMLドキュメントに独自の名前空間がある場合、スタイルシートで名前空間を宣言し、それに接頭辞を割り当て、ソースXMLの要素を参照するときにその接頭辞を使用する必要があります。たとえば:

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

または、必要に応じて:

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

補遺:XML名前空間が事前にわからない非常にまれなケースでは、次のことができます。

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

もちろん、ソースXML文書に「property」という名前の要素が含まれており、置換が必要な「type」という名前の属性があることを事前に知っているシナリオを想像することは非常に困難ですが、文書の名前空間はまだわかりません。これは主に、独自のソリューションを合理化する方法を示すために追加しました。

1
michael.hor257k

私も同じ問題に遭遇し、次のように解決しました:

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