Sun, December 27, 2009
XSLTで子要素(テキスト)を属性にする記述、およびその逆変換
このXSLのイディオムはあまりによく使うのでメモ。
やりたいこと その1 子要素を属性値にする
title要素の子要素(テキスト)を value の属性値に設定する。
FrameMakerでは、章タイトルをボディページではなく、マスターページに記述する場合があり、 そのときによく使うテクニックです。
変換前
<?xml version="1.0" encoding="UTF-8"?>
<book>
<title>設置と接続</title>
</book>
変換後
<?xml version="1.0" encoding="UTF-8"?>
<book>
<title value="設置と接続" />
</book>
変換前から変換後を作り出す conv.xsl
<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>
<!-- title要素の内容を value属性に入れる -->
<xsl:template match="title" >
<xsl:element name="{name()}" >
<xsl:copy-of select="@*" />
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
変換を実行
$ xsltproc conv.xsl sample.xml
結果が標準出力されます。
やりたいこと その2 属性値を子要素にする
要は先ほどの逆。 結果として元に戻る。
逆の変換は、 XML RoundTripをする上では必須テックニック。
変換後から変換前に戻す reconv.xsl
<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>
<!-- title要素の内容を value属性に入れる -->
<xsl:template match="title" >
<xsl:element name="{name()}" >
<xsl:for-each select="@*" >
<xsl:if test=" not(name()='value') " >
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
<xsl:value-of select="@value"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
ポイント
xsl:for-each を使って、属性を一つずつ取り出し、value 属性だけを除外する部分。
<xsl:for-each select="@*" >
<xsl:if test=" not(name()='value') " >