次のコードは、xml を csv に変更します。
以下のサンプルデータに当てはめると、
<Addy>
<Row>
<L>1</L>
<LD>Dwelling</LD>
<Th>Passage</Th>
</Row>
</ADDY>
列名を含むこの形式のcsvファイルを生成します
L,LD,Th
1,Dwelling,Passage
アイデアは、期待される結果が
1,Dwelling,passage,
削除する
<xsl:for-each select="*/*/*[generate-id() = generate-id(key('field',name())[1])]">
<xsl:value-of select="name()" />
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
<xsl:text> </xsl:text>
template
forから、/
コンマを追加します。
<xsl:text>, </xsl:text>
forinモードtemplate
で*
。row
要素がすべて同じ順序である場合Row
、複雑な Muenchian Grouping やテンプレート モードなどは必要ありません。非常に単純なスタイルシートだけで十分です。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<xsl:template match="Row">
<xsl:apply-templates select="*" />
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="Row/*">
<xsl:value-of select="." />
<xsl:text>,</xsl:text>
</xsl:template>
</xsl:stylesheet>
次の入力でこれを実行します。
<Addy>
<Row>
<L>1</L>
<LD>Dwelling</LD>
<Th>Passage</Th>
</Row>
<Row>
<L>2</L>
<LD>Foo</LD>
<Th>Bar</Th>
</Row>
</Addy>
最後の列値の後の末尾のコンマを含め、目的の出力を生成します。
1,Dwelling,Passage,
2,Foo,Bar,
テンプレート マッチングを次の/
ように変更するだけです。
<xsl:template match="/">
<xsl:apply-templates select="*/*" mode="row"/>
</xsl:template>
そして、生成された各行の最後に末尾のコンマが本当に必要な場合は、次の最初の出現を置き換えます。
<xsl:if test="position() != last()">,</xsl:if>
と:
<xsl:text> </xsl:text>
これらの最小限の変更のみを実行すると、提供されるコードは次のようになります。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="field" match="/*/*/*" use="name()" />
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="*/*" mode="row"/>
</xsl:template>
<xsl:template match="*" mode="row">
<xsl:variable name="row" select="*" />
<xsl:for-each select="/*/*/*[generate-id() = generate-id(key('field',name())[1])]">
<xsl:variable name="name" select="name()" />
<xsl:apply-templates select="$row[name()=$name]" mode="data" />
<xsl:text>,</xsl:text>
</xsl:for-each>
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="*" mode="data">
<xsl:choose>
<xsl:when test="contains(text(),',')">
<xsl:text>"</xsl:text>
<xsl:call-template name="doublequotes">
<xsl:with-param name="text" select="text()" />
</xsl:call-template>
<xsl:text>"</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:template>
<xsl:template name="doublequotes">
<xsl:param name="text" />
<xsl:choose>
<xsl:when test="contains($text,'"')">
<xsl:value-of select="concat(substring-before($text,'"'),'""')" />
<xsl:call-template name="doublequotes">
<xsl:with-param name="text" select="substring-after($text,'"')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
この変換が次の XML ドキュメントに適用されると (2 つのRow
要素の子の順序が異なることに注意してください)。
<Addy>
<Row>
<L>1</L>
<LD>Dwelling</LD>
<Th>Passage</Th>
</Row>
<Row>
<Th>Bar</Th>
<LD>Foo</LD>
<L>2</L>
</Row>
</Addy>
必要な正しい結果が生成されます。
1,Dwelling,Passage,
2,Foo,Bar,