XSLT:< xsl:copy>中に属性値を変更する方法は?
質問
XMLドキュメントがあり、属性の1つの値を変更したい。
最初に、入力から出力にすべてをコピーしました:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
そして、&quot; property&quot;
という名前の要素の属性&quot; type&quot;
の値を変更したいです。
解決
簡単な例でテストされ、正常に動作します:
<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>
トマラックの提案を含めるように編集。
他のヒント
この問題には古典的な解決策があります: アイデンティティテンプレート は、最も基本的かつ強力なXSLTデザインパターンの1つです:
<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>
ルート要素に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>
ターゲット属性に一致するテンプレートのみが必要です。
<xsl:template match='XPath/@myAttr'>
<xsl:attribute name='myAttr'>This is the value</xsl:attribute>
</xsl:template>
これは、「すべてコピー」に追加されます。あなたはすでに持っています(実際、XSLTにはデフォルトで常に存在しています)。より具体的な一致が優先的に使用されます。
同様のケースで、単純なノードから1つの属性を削除したいのですが、どの軸で属性名を読み取れるのかわかりませんでした。結局、私がしなければならなかったのは、使用することだけでした
@ * [name(。)!= 'AttributeNameToDelete']
次の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>
ソース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ドキュメントに&quot; type&quot;という名前の属性を持つ&quot; property&quot;という名前の要素が含まれていることを事前に知っているシナリオを想像することは非常に困難です。置き換える必要がありますが、それでもドキュメントの名前空間はわかりません。これは主に、独自のソリューションを合理化する方法を示すために追加しました。
私も同じ問題に遭遇し、次のように解決しました:
<!-- 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>