指向性メモ::2004-11-26

ページ情報
制作日
2004-11-26T14:26:05+09:00
最終更新日
2004-11-26T23:13:03+09:00
ページ内目次

XSLTで効率的にインデントする方法

Created:
2004-11-26T14:26:05+09:00

前にもどこかで書いたが、正規表現などによるXML文章の直接操作は危険なのでやめた方が無難だ。XML文章をいじりたいならばDOMなどのAPIを叩くべきである。というのも、例えばCDATAセクション内の、本来はいじりたくない部分まで置換されてしまったりと、細かいところで問題が起こる可能性があるからだ。XMLは微妙に複雑なので出来るだけ文字列としての扱いは避け、パースしてから扱ったほうが良いといえるだろう。

といわけで、せっかくXSLTを使うなら、もう1度インデントの為のXSL変換を行うの方法をお勧めする。メインの処理はインデント無しで行い、最後にインデントだけ行うXSL文章で整形する事により、かなり自由度の高いインデントが可能になる。参考までにHIMMMELでインデント用に使っているXSL文章は次のような感じだ。

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:xh="http://www.w3.org/1999/xhtml">

<xsl:output omit-xml-declaration="no" version="1.0" method="xml" indent="no" encoding="UTF-8"/>

<xsl:param name="spacer" select="'  '"/>



<xsl:template match="*[ancestor::*[./@xml:space][1]/@xml:space = 'preserve']
                     | *[ancestor::xh:pre]" priority="15">
    <xsl:element name="{local-name()}" namespace="{namespace-uri(.)}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="*[./@xml:space = 'preserve']
                     | xh:pre" priority="14">
    <xsl:call-template name="space"/>
    <xsl:element name="{local-name()}" namespace="{namespace-uri(.)}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
    <xsl:text>
</xsl:text>
</xsl:template>

<xsl:template match="*[following-sibling::text() or preceding-sibling::text() or ancestor::*[following-sibling::text() or preceding-sibling::text()]]" priority="13">
    <xsl:element name="{local-name()}" namespace="{namespace-uri(.)}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="*[not(child::node())]" priority="12">
    <xsl:call-template name="space"/>
    <xsl:element name="{local-name()}" namespace="{namespace-uri(.)}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
    <xsl:text>
</xsl:text>
</xsl:template>

<xsl:template match="*[child::text()]" priority="11">
    <xsl:call-template name="space"/>
    <xsl:element name="{local-name()}" namespace="{namespace-uri(.)}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
    <xsl:text>
</xsl:text>
</xsl:template>

<xsl:template match="*[not(child::text())]" priority="10">
    <xsl:call-template name="space"/>
    <xsl:element name="{local-name()}" namespace="{namespace-uri(.)}">
        <xsl:copy-of select="@*"/><xsl:text>
</xsl:text>
        <xsl:apply-templates/>
        <xsl:call-template name="space"/>
    </xsl:element>
    <xsl:text>
</xsl:text>
</xsl:template>


<xsl:template match="comment()[ancestor::comment()[./@xml:space][1]/@xml:space = 'preserve']
                     | comment()[ancestor::xh:pre]" priority="7">
    <xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="comment()[following-sibling::text() or preceding-sibling::text() or ancestor::*[following-sibling::text() or preceding-sibling::text()]]" priority="6">
    <xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="comment()" priority="5">
    <xsl:call-template name="space"/>
    <xsl:copy-of select="."/>
    <xsl:text>
</xsl:text>
</xsl:template>


<xsl:template name="space">
    <xsl:for-each select="ancestor::*">
        <xsl:value-of select="$spacer"/>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

気合があれば全てのブロック要素とインライン要素をリストアップすることも出来るが、XHTML1.1の場合、これで大体思ったとおりにインデントできる。このソース自体はどの語彙でも使えるが、XHTMLだけはちょっと贔屓して、pre要素を明示的にxml:space="preserve"として扱っている。また、諸事情により変換後の要素名が{local-name()}になっているが、名前空間接頭辞を保持したいならば{name()}に変えればいいはずだ。

1つ問題点を挙げるとしたら、次のようなテキストノートを兄弟に持つブロックレベル要素が現れるとそのブロックレベル要素の中身がインデントされない。

<ul>
    <li>foo</li>
    <li>bar
        <ul><!-- ul要素の兄弟にテキストノードが! -->
            <li>baz</li>
        </ul>
    </li>
</ul>

このようなソースは次のように変換される。

<ul>
    <li>foo</li>
    <li>bar<ul><li>baz</li></ul></li>
</ul>

要素を全てリストアップしてブロックレベル要素とインライン要素に分類すれば解決できるが、さすがに面倒な上、XHTML1.1ではul要素かobject要素以外ではこのような状況になることが有り得ないので放置していいと思う。XHTML2.0だとp要素に色々なブロックレベルが含まれるようになるので適切にインデントしたいならば手直しが必要になる。

Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo/2004/11/26/142605

素朴な疑問

Created:
2004-11-26T21:15:04+09:00
Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo/2004/11/26/211504

入門RSS

Created:
2004-11-26T22:46:02+09:00

本屋に行ったら『入門RSS―Webにおける効率のよい情報収集/発信』なる本が置いてあったので軽く立ち読み。

メインはRSS1.0だけどXMLの基本からRDFの仕組みまで簡単に説明してあった。後半部分ではFOAFなどのRDF応用系の語彙も解説してあって、セマンティックウェブ方面の入門書には良さそう。

ところで、AtomってRDF準拠じゃないのね。世の中RDFが大ブームかと思いきや、やっぱり複雑で敬遠されてるのかな。便利なようで結構メンドくさいしね。

Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo/2004/11/26/224602

Ever17が超安くなってた

Created:
2004-11-26T23:13:03+09:00

新品で1785円になってたのか。すげえ。

神ゲーともっぱらの評判なので是非是非プレーしてみてください。ただのギャルゲーじゃないです。

Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo/2004/11/26/231303
連絡先、リンク、転載や複製などについては『サイト案内』をご覧ください。Powered by HIMMEL

I ♥ Validator