「XSLTでXMLをHTMLに変換する」という内容でググると
大抵「まずはやってみよう」的なコーナーからxsl:template→xsl:apply-templatesで書き出していくのが見当たるけど、
これはXMLを上から下に向かって「降りていく」ような動きをするので、
つまり一度通り過ぎたタグをそれよりさらに下の階層から参照する、というのが
「まずはやってみよう」に載っている一般情報的なものではできない。
そのタグのときにやるべきことはすべてやっておけという感じが強い。
でも得てして、通り過ぎたタグ情報を参照したくなるケースがある。
あるいは、現在タグ位置等無視して全然違う内容を呼び出したい、
ということもある。
そういう場合の書き方を自分用にメモる。
まず対象のXMLは以下の通り。↓
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="test_original.xsl"?> <TEST> <VALUE>TEST VALUE.</VALUE> <CONTENTS> <DETAIL_INFO> <NO>1</NO> <NAME>TEST_NUMBER_1</NAME> </DETAIL_INFO> <DETAIL_INFO> <NO>2</NO> <NAME>TEST_NUMBER_2(あああ)</NAME> </DETAIL_INFO> <DETAIL_INFO> <NO>3</NO> <NAME>TEST_NUMBER_3(WWW@@@)</NAME> </DETAIL_INFO> </CONTENTS> </TEST>
これを↓のXSL(test_original.xls)と、
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="html" encoding="UTF-8"/> <xsl:template match="/"> <xsl:apply-templates/> </xsl:template> <xsl:template match="TEST"> <html> <head> <title>TEST TITLE</title> <link rel="stylesheet" type="text/css" href="TEST_XML_STYLE_SHEET.css" /> <style type="text/css"> <xsl:comment> font.test { color:blue; font-weight:bold; } .clear_style { color:initial; } </xsl:comment> </style> </head> <body> <h1 class="h1_test clear_style">TEST HEAD 1</h1> <font class="test">This is stylesheet test.</font> <xsl:apply-templates select="CONTENTS"/> </body> </html> </xsl:template> <xsl:template match="CONTENTS"> <br/> <font color="green" size="5"> Original XSL 'CONTENTS' ELEMENT HIT!<br/> <xsl:value-of select="../VALUE" /></font> <br/> <xsl:apply-templates select="DETAIL_INFO" mode="org1" /> <xsl:apply-templates select="DETAIL_INFO" mode="org2" /> コールします。<br/> <xsl:call-template name="include_root" /> </xsl:template> <xsl:template match="DETAIL_INFO" mode="org1"> <br/> <font color="yellow" size="5"> Original XSL 'DETAIL_INFO' ELEMENT HIT!<br/> </font> <br/> </xsl:template> <xsl:template match="DETAIL_INFO" mode="org2"> <br/> <font color="mediumyellow" size="5"> Original XSL 'DETAIL_INFO' ELEMENT HIT!2<br/> </font> <br/> </xsl:template> <xsl:include href="test_include.xsl" /> </xsl:stylesheet>
↑からincludeされるこのXSL(test_include.xls)↓
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template name="include_root"> <hr/> <font color="blue" size="7">Include root Element By 'call-template'!!</font><br/> <br/> <xsl:apply-templates select="/" mode="include0" /> <!-- <xsl:apply-templates/> --> </xsl:template> <xsl:template match="/" mode="include0"> <font size="4" color="purple">include root test.</font> <br/> <xsl:apply-templates select="TEST" mode="inlcude_test"/> </xsl:template> <xsl:template match="TEST" mode="inlcude_test"> <br/> Include XSL 'TEST' ELEMENT HIT!<br/> <xsl:apply-templates select="CONTENTS" mode="include_contents"/> <br/> </xsl:template> <xsl:template match="CONTENTS" mode="include_contents"> <br/> Include XSL 'CONTENTS' ELEMENT HIT!<br/> <xsl:apply-templates select="DETAIL_INFO" mode="include_detail_info"/> <br/> </xsl:template> <xsl:template match="DETAIL_INFO" mode="include_detail_info"> <BR/> Include XSL 'DETAIL_INFO' ELEMENT HIT!<br/> <xsl:value-of select="NO"/>:<xsl:value-of select="NAME"/> <BR/> </xsl:template> </xsl:stylesheet>
で変換かけていく。
今回変換対象となるXMLのタグ構造をツリー上に表現すると
TEST
├VALUE
└CONTENTS
└DETAIL_INFO
├NO
└NAME
というかんじになる。
「まずやってみよう」のやり方で上から順に読み解いていく場合、
例えば「DETAIL_INFO」までいっちゃうと「VALUE」は通り過ぎてるので読めない。
これを可能にする方法のメモ、ということ。
XSLで処理する際、もとのXMLはUNIX OSのディレクトリパスとして意識するとわかりやすい。
最上層の「TEST」をルートからの最初のディレクトリと見做せば、
「CONTENTS」は「/TEST/CONTENTS」になる。
◆「CONTENTS」処理中に「VALUE」の中身を見る
「test_original.xsl」の以下の記述で無理やり「VALUE」の中身を参照している。
<xsl:value-of select="../VALUE" />
↑で書いた「ディレクトリパス」的表現に基づき、
「CONTENTS」内から見れば”1階層上(="../VALUE")にいる「VALUE」”を参照して
その値を取得して表示する記述になっている。
実際これで取れる。
もしくは
<xsl:value-of select="/TEST/VALUE" />
↑これでもいける。
(現在処理中のタグ位置を意識しなくていい(どのタグを処理中でもいける)ので、こっちのほうがいいかもしれない)
◆最初からやり直す<
一度上から下まで(一番下までじゃなくてもいいけど)全部読み切ったあと、
また最初から、今度は別の定義に基づいて変換をかけていく、
というのをやりたい場合、
xsl:call-templateで一度全てをリセットしてしまえばいい。
この処理では「test_original.xsl」で「DETAIL_INFO」まで降りた後、
「test_include.xsl」のほうに処理を飛ばしてそこでもう一度ルートからやり直している。
○「test_original.xls」の該当の記述
<xsl:template match="CONTENTS"> <br/> <font color="green" size="5"> Original XSL 'CONTENTS' ELEMENT HIT!<br/> <xsl:value-of select="../VALUE" /></font> <br/> <xsl:apply-templates select="DETAIL_INFO" mode="org1" /> ←(1)自分とこの「DETAIL_INFO」分処理1 <xsl:apply-templates select="DETAIL_INFO" mode="org2" /> ←(2)自分とこの「DETAIL_INFO」分処理2 コールします。<br/> <xsl:call-template name="include_root" /> ←(3)「test_include.xls」のほうに飛ばす </xsl:template>
○「test_include.xls」の該当の記述
<xsl:template name="include_root"> ←(4)「test_original.xls」のcall-templateでこっちに飛んでくる <hr/> <font color="blue" size="7">Include root Element By 'call-template'!!</font><br/> <br/> <xsl:apply-templates select="/" mode="include0" /> ←(5)ここで自分のルート処理(別モードで定義)に飛ばす <!-- <xsl:apply-templates/> --> </xsl:template> <xsl:template match="/" mode="include0"> ←(6)自分のルート処理の開始。ここから改めて”また上から”処理を開始できる。 <font size="4" color="purple">include root test.</font> <br/> <xsl:apply-templates select="TEST" mode="inlcude_test"/> </xsl:template>
という感じ。
「test_original.xls」で処理していた(処理しきった)状態を
「test_include.xls」でリセットしているかんじ。
これは頭からやり直すパターンだが、call-templateで外に飛ばせば
別にどこからでも開始できる。便利。(飛ばしまくると意味不明になりそうだが)