xsltproc
XMLをDBのテーブルに格納したい時はDBのツールを利用するために、一旦CSVに変換したくなる。XMLからCSVへの変換っていうと、プログラムを組まなきゃって思うけど、xsltprocを使うとXSLTファイルで変換マッピングを作ってあげれば、シェルからでも変換処理ができる。
http://xmlsoft.org/XSLT/xsltproc.html
フラットな構造をCSVに展開する
まずはそのままCSVに展開できそうなフラットなデータ構造の場合を考えよう。planets1.xml
<?xml version="1.0" encoding="utf-8"?> <planets> <planet> <name>水星</name> <diameter>4879.4</diameter> <mass>3.301e+23</mass> <epoch>2008-01-01</epoch> </planet> <planet> <name>金星</name> <diameter>12103.6</diameter> <mass>4.869e+24</mass> <epoch>2008-01-01</epoch> </planet> <planet> <name>火星</name> <diameter>6794.4</diameter> <mass>6.4191e+23</mass> <epoch>2008-01-01</epoch> </planet> <planet> <name>木星</name> <diameter>142984</diameter> <mass>1.8986e+27</mass> <epoch>2008-01-01</epoch> </planet> </planets>
これを planet 要素毎にCSVレコードにするXSLTは以下のようになる。
planets2csv1.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="utf-8" /> <xsl:param name="delim" select="','" /> <xsl:param name="quote" select="'"'" /> <xsl:param name="break" select="' '" /> <xsl:template match="/"> <xsl:apply-templates select="planets/planet" /> </xsl:template> <xsl:template match="planet"> <xsl:apply-templates /> <xsl:value-of select="$break" /> </xsl:template> <xsl:template match="*"> <xsl:value-of select="concat($quote, text(), $quote)" /> <xsl:if test="following-sibling::*"> <xsl:value-of select="$delim" /> </xsl:if> </xsl:template> <xsl:template match="text()" /> </xsl:stylesheet>
$ xsltproc planets2csv1.xsl planets1.xml "水星","4879.4","3.301e+23","2008-01-01" "金星","12103.6","4.869e+24","2008-01-01" "火星","6794.4","6.4191e+23","2008-01-01" "木星","142984","1.8986e+27","2008-01-01"
各要素をダブルクォートで囲む必要がなければ、select="concat($quote, text(), $quote)" を select="text()" に変えればいい。
親要素があるような構造をCSVに展開する
今度は各データに共通な親要素があるようなデータ構造を考えよう。内惑星と外惑星にカテゴリ分けしたXMLにしてみよう。
planets1.xml
<?xml version="1.0" encoding="utf-8"?> <list> <orbits> <orbit>内惑星</orbit> <planets> <planet> <name>水星</name> <diameter>4879.4</diameter> <mass>3.301e+23</mass> <epoch>2008-01-01</epoch> </planet> <planet> <name>金星</name> <diameter>12103.6</diameter> <mass>4.869e+24</mass> <epoch>2008-01-01</epoch> </planet> </planets> </orbits> <orbits> <orbit>外惑星</orbit> <planets> <planet> <name>火星</name> <diameter>6794.4</diameter> <mass>6.4191e+23</mass> <epoch>2008-01-01</epoch> </planet> <planet> <name>木星</name> <diameter>142984</diameter> <mass>1.8986e+27</mass> <epoch>2008-01-01</epoch> </planet> </planets> </orbits> </list>
これをCSVに変換するXSLTは以下のようになる。
planets2csv2.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="utf-8" /> <xsl:param name="delim" select="','" /> <xsl:param name="quote" select="'"'" /> <xsl:param name="break" select="' '" /> <xsl:template match="/"> <xsl:apply-templates select="/list/orbits/planets" /> </xsl:template> <xsl:template match="planets"> <xsl:apply-templates select="planet" /> </xsl:template> <xsl:template match="planet"> <xsl:variable name="orbit" select="string(../../orbit/text())" /> <xsl:value-of select="concat($quote, $orbit, $quote)" /> <xsl:value-of select="$delim" /> <xsl:apply-templates /> <xsl:value-of select="$break" /> </xsl:template> <xsl:template match="*"> <xsl:value-of select="concat($quote, text(), $quote)" /> <xsl:if test="following-sibling::*"> <xsl:value-of select="$delim" /> </xsl:if> </xsl:template> <xsl:template match="text()" /> </xsl:stylesheet>
ポイントは planet のテンプレート部分で親要素を参照しているところ。
相対パスで親要素を参照すれば、共通ヘッダの内容も繰り返して出力できる。
<xsl:template match="planet"> <xsl:variable name="orbit" select="string(../../orbit/text())" /> <xsl:value-of select="concat($quote, $orbit, $quote)" />
$ xsltproc planets2csv2.xsl planets2.xml "内惑星","水星","4879.4","3.301e+23","2008-01-01" "内惑星","金星","12103.6","4.869e+24","2008-01-01" "外惑星","火星","6794.4","6.4191e+23","2008-01-01" "外惑星","木星","142984","1.8986e+27","2008-01-01"
参考サイト
https://stackoverflow.com/questions/365312/xml-to-csv-using-xslt
0 件のコメント:
コメントを投稿