HTML5はXHTMLで楽に書ける?XSLTによるMVCライクなフレームワーク framexs を開発したので解説するよ
XSLTフレームワーク:framexsバージョン1
初めに
私はHTMLを書くのが好きです。テキストデータを解析し高度な文書データとして創りあげ構成していく過程と完成された結果は創作意欲を満足させるものでした。しかし、一方で何かしらの理由であれHTMLを書くときはだいたいの場合において冗長で単調な作業にうんざりする事が多かったのも事実です。共通した構成やコードを持つ何枚ものHTMLファイルを作るときや修正を行うときは何の面白みもないコピペを繰り返していました。それが複雑で大きなものならそのコードの中から書き換えるべき箇所を見つけ出す作業を強いられていました。DreamWeaverのテンプレート機能は便利でしたがDWはお手軽に手に入るものではないしその機能はDW以外では使えません。それがもっと気軽にできれば、と何度も思っていました。そういった積み重なった思いから効率的にそして分り易い仕組みのツールや方法はないかと思案していました。そしてさまざまな方法を考え試していくうちに、ついにそれらを多くの部分で解決できる仕組みのフレームワークを創れたのではないかと思うに至りました。それがこれから解説していくframexsバージョン1(これ以降は単にframexsと表記します)です。
framexsとは
まずはframexsでどんな魔法を起こせるのかを見てみましょう。このリンクのページを見てください。
これは私の自己紹介です。そしてこのページのソースを見てください。
もしあなたがHTMLあるいはXHTMLを知っているなら、違和感を覚えるはずです。本来なら表示されるはずのないものが表れていることに気づくでしょう。この現象に面すれば多くの人はJavaScriptで魔法を起こしていると考えるかもしれません。そうなのでしょうか?いいえ、この魔法を起こしているのがXSLTプロセッサーでありこれから解説していくframexsなのです。
要約するとframexsは主にブラウザーからウェブサイトにアクセスして(X)HTMLを読む場合を想定し、XHTMLのコンテンツとテンプレートから柔軟に効率的にHTMLを作りそれを読ませる目的のフレームワークです。XSLT 1.0で書かれていてXSLTプロセッサーに処理を行わせます。
最低限の仕様とAPIはGitHubのframexsリポジトリのREADMEに書かれています。
しかし、これだけでは具体的な使い方は分からないと思いますので、この記事でサンプルを使いながら使い方などを解説をしていきたいと思います。ただし、基本的にはこれからの解説のなかではこれをきちんと読んでいることを前提にしているので適宜に読み返してください。
対象読者
その前にいくつかの注意点があります。
この記事を理解するにあたってHTMLを知っておくのは当然として、XMLの仕様についてもある程度知っておく必要があります。この記事でもいくつかの部分に少し解説は行いますが基本的にはこの記事を理解するだけのXMLの仕様を知っていることを前提に解説を行っていきます。
また、ただ単に利用するだけなら必須ではありませんが、フレームワークの仕組みを理解するにはXSLTが何であるかを知っておく必要があります。
しかし、ここでは特にはXMLやXSLTの解説は行いません。ですので、わからない場合は各自で調べ学習してください。また、何かの事について解説を省く旨の事がこれからもありますが、求めることは上記と同じで各自で調べるなどしてください。
準備
解説をするにあたって充分に理解をしてもらうためには読むだけではなかなか理解は難しいと思われますので、いくつかの準備をしてもらうことになります。
ファイルのダウンロード
framexsおよびこの記事の解説に出てくるサンプルは上記のGitHubのframexsリポジトリにありますのでダウンロードする場合は適宜に利用してください。
解説ではGitを利用する前提で話を進めます。ただし、この記事ではGitの使い方やコマンドで具体的にどう打つかなどについては詳説しません。
ウェブサーバー
ブラウザーからウェブサーバーを通してframexs特有の記述をしたXHTMLコンテンツファイルにアクセスする場合を想定しており、この記事に書いてあることを試すには基本的にはウェブサーバーを用意して起ち上げる必要があります。
サーバーのMIMEタイプの設定によっては上手く表示されない場合もあるかもしれませんので注意してください。
この記事ではウェブサーバーの仕組みや用意の仕方や起ち上げ方法などのことについては解説しません。
また、ウェブサーバーを通さないローカルのファイルシステム上でサンプルファイルなどがどうなるかは解説しません。
推奨はしませんが、ウェブサーバーを立ち上げずにブラウザで結果だけ見たいのであればGitHub Pageで見ることもできます。
例えばリポジトリの/example/helloworld/template.xhtml
をブラウザで見るなら https://nandaka-furari.github.io/framexs/example/helloworld/template.xhtml にアクセスしてください。
XSLTツール
ウェブサーバーからサンプルなどをリソースとしてアクセスする場合、アクセスと表示の間にXSLT処理を挟むことがあります。通常のテキストエディターとブラウザーを使用するだけでは生成されたHTMLの生のコードを確認することができないはずです。ブラウザーで見るだけでは実際のコードを確認することは難しいので、そうすると、仕様の理解が充分にできない所が多々出てくると思われます。その問題を解決するためには任意の出力先を持てるXSLT処理の機能を持ったツールが確認を容易にするために必要になってきます。
これはjEditがおすすめです。Mac OSで使われることのあるJeditではありません。jEditはJava製のテキストエディターで、XSLTプラグインを入れることによって任意のXSLT処理を行わせることができるので、私はHTMLの出力結果の確認をするときに頻繁に使っていました。また、今回の事に関するコードを書くときももちろん、それ以外でも愛用しています。この記事ではjEditのインストール方法や使い方などの解説は省きますが、今回のケースに限らずあらゆる場面で役に立つツールだと思っています。
その他必要なこと
今までの説明の通りウェブサイトにアクセスするのでブラウザーを使うことになりますが、いわゆる開発者ツールについて使い方を知っておく必要があります。そうでなければ、より効率的に理解する事は難しいと思われます。特にXSLTツールがない状況では開発者ツールに頼ることになります。
開発環境
次に参考として私の開発環境を実際の所とは一部を変えて示します。hogeの部分は実際には別の文字列です。この環境であることを前提に話を進めていきます。必要に応じて各自の環境で読み替えてください。
- OS
- Windwos 10 64bit
- ウェブサーバー
- Jetty 9.3.11 http://www.eclipse.org/jetty/
- サーバーへのアクセス
- http://localhost:8080/
- Gitのローカルリポジトリ
- C:\Users\hoge\workspace\nandaka-furari.github.io\framexs
- ウェブサーバーのドキュメントルート
- C:\Users\hoge\workspace\nandaka-furari.github.io
- GitHub Page
- https://nandaka-furari.github.io/framexs
- テキストエディター兼XSLT出力ツール
- jEdit 5.4.0
- ブラウザ
- Google Chrome
- Java
- OpenJDK Runtime Environment (build 1.8.0_131-1-redhat-b11)
ブラウザーで http://localhost:8080/framexs/framexs.xsl
にアクセスするとファイルシステム上の C:\Users\hoge\workspace\nandaka-furari.github.io\framexs\framexs.xsl
にアクセスするような形になります。
GitHub Pageなら https://nandaka-furari.github.io/framexs/framexs.xsl です。
使い方
では、具体的に何に何をするのかを実際のコードを例にしながら解説します。例となるファイルは/example以下のディレクトリにあります。
ハローワールド /example/helloworld
名前がtemplate.xhtmlのテンプレートとして使うXHTMLファイルと、名前がcontent.xhtmlのコンテンツデータとして使うXHTMLファイルがあり、また、リポジトリのトップに名前がframexs.xslのframexsフレームワークとしての処理を記述したXSLファイルがあります。
XHTMLについて実際のコード見ていきます。まずはコンテンツデータを見てみましょう。
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="application/xml" href="../../framexs.xsl"?>
<?framexs.skeleton template.xhtml?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>世界よこんにちは</title>
</head>
<body>
<div id="main">
<p>ハローワールド!</p>
</div>
</body>
</html>
基本的にはXHTML1.0 StrictのXHTMLですが普通とは違うところもあります。
<?xml-stylesheet type="application/xml" href="../../framexs.xsl"?>
この部分はProcessing Instruction(処理命令)です。コードの意味はこの記述があるXMLにスタイルシートを設定するというものです。XHTMLもXMLなのでlink要素の代わりにこの記述方法でスタイルシートを指定することもできます。この指定方法ではスタイルシートの種類にはCSSとXSLのどちらかを指定できます。またこの場合はXMLプロローグにコードを記述しておく必要があります。
ここではframexsのXSLファイルをスタイルシートとして指定しています。
そのために、このファイルをウェブサーバーを通してブラウザで読み込むと、そのままパースしてレンダリングを行うのではなく、その前にまずXSLT処理を行おうとします。
XMLの仕様のうちの一つである処理命令に関してはframexsを使う上である程度知っておく必要があります。
<?framexs.skeleton template.xhtml?>
この処理命令はframexsが解釈する記述がされています。このframexs.skeletonという記述はテンプレートXHTMLを使うという宣言であり、そのためのファイルの場所としてtemplate.xhtmlを指定しています。framexsはXSLT処理時にこのファイルをXSLTプロセッサーにロードさせます。このコードのようにプロローグ部の処理命令で先頭にframexs.がついたものをframexsコマンドと呼びます。
次にテンプレートを見ていきます。
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:framexs="urn:framexs" xml:lang="ja" lang="ja">
<head>
<title> - テンプレートタイトル</title>
</head>
<body>
<h1>あいさつ</h1>
<article><p framexs:id-d="main">コンテンツのid属性がmainの部分を読み込み置き換える</p></article>
</body>
</html>
テンプレートは基本的には普通のXHTMLですが、ルート要素でurn:framexsという名前空間が宣言されており、p要素でその名前空間の属性の framexs:id-d
が使われています。この指定はこの部分でコンテンツ値がmainのidを持つ要素の子孫ノードと置き換えることを指示しています。
framexsではこのようにframexsの名前空間を持つ属性でテンプレートに機能を持たせます。注意点としてテンプレートには一般的に使われているDTDやXML Schemaのようなスキーマを持たせるべきではないということです。framexs属性は一般的なスキーマを持ったXHTMLとしては不正です。もし検証を行う機能を持ったパーサーであればエラーになるでしょう。
そして、これらを組み合わせ変換を行わせるのがframexsです。
そして変換結果はおおよそ次のようになります。処理系によって若干違う可能性があり正確にこうならないかもしれません。jEditでは空行や空白などが生成されましたがここでは見やすさのためにそれらを適宜に削除しています。
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>世界よこんにちは - テンプレートタイトル</title>
</head>
<body>
<h1>あいさつ</h1>
<article>
<p>ハローワールド!</p>
</article>
</body>
</html>
テンプレートとコンテンツが合成されたHTMLになります。
title要素のテキストはコンテンツのtitle要素のテキストにテンプレートのtitle要素のテキストを加えたものになっています。合成するテキストの順番は固定です。
ここで注意が必要なのは、テンプレートの中でframexs属性のうちのいくつかはある要素のうちに一つだけ設定されていることを想定しています。例えば
<p framexs:id-d="main" framexs:fetch-d="content"><p>
のようなコードは不正です。これら二つの属性は同時に使用はしないでください。このような場合にどうするかは決めておらず、上手く動くかどうかの検証も行っておりませんし、今のところ今後も行わないつもりです。
どのframexs属性がそのような性質を持つかは仕様を読んでください。
そしてブラウザーで http://localhost:8080/framexs/example/helloworld/content.xhtml
にアクセスしてみてください。読み込みを行いこの結果HTMLをパースしたブラウザーはまるで初めからこのコードのファイルがあったかのように画面を表示します。
自前のウェブサーバーを用意していないなら https://nandaka-furari.github.io/framexs/example/helloworld/content.xhtml
を見てください。
また、 http://localhost:8080/framexs/template.xhtml
にアクセスするとテンプレートもXHTMLなのでブラウザーで普通に読めます。テンプレートと結果HTMLを比較できるのです。この事は重要な点です。この例だけではあまり伝わりませんがテンプレートがテンプレートの解説を行うドキュメントそのものとして存在させることができるという事です。
この処理方法はMVCパターンといえると思います。モデルはコンテンツでビューはテンプレート、コントローラーはframexsとコンテンツのframexsコマンドに相当します。XSLは重要な役目を負っていますがただ単に利用するだけならXSLコードは一切書く必要はありません。framexsにおいてXSLを書く役割りを持つのは私だけだからです(今のところは、ですが。XSLを書くのに協力してくれる方がいれば大歓迎します)。
発展 /example/developement
次にexample/developementにあるファイルを見てください。ここからはコードの貼り付けは行いませんので実際にコードを見てください。
出力結果を確認するのにXSLTツールが用意できない場合はブラウザーの開発ツールからDOMの構成を確認するなどしてください。
テンプレートにいくつかのframexs属性が使われています。framexsページの一覧を参照しつつどのように変換されているかを確認しましょう。
まずhead要素以下のコードを見てみましょう。
template.xhtmlとcontent.xhtmlのtitle要素のテキストとmeta要素の同じ値のname属性を持つかまたは同じ値のproperty属性のcontent属性値が合成されています。ただし、テンプレートおよびコンテンツで存在しない名前属性値のmeta要素は生成されていません。
また、テキストが合成されるルールはコンテンツ+テンプレートです。この順番は固定で変更できません。
body要素以下のコードをそれぞれ見ていきましょう。
テンプレートでいくつかのframexs属性が使われていますので、それぞれどのような動作をするかを確認してください。
パスの解決 /example/path
いままでの例ではテンプレートとコンテンツは同じ階層のディレクトリにありました。それらが別の階層だった場合にどうなるかをexample/pathで解説していきます。
ここではテンプレートが3つ用意されています。
- template_a.xhtml
- dir1/template_b.xhtml
- dir1/dir2/template_c.xhtml
それぞれに<script src="">``<a href="">``<object data="">``<link href="">
といったタグがあります。また、コンテンツとしていくつかのファイルがあります。
- utc.xhtml
- dir1/uta.xhtml
- dir1/utb.xhtml
- dir1/utc.xhtml
- dir1/dir2/uta.xhtml
そのうちの一つのutc.xhtmlを見てみましょう。utc.xhtmlには<?framexs.skeleton dir1/dir2/template_c.xhtml?>
とコードがあり別のディレクトリのテンプレートを指定しています。
合成されたHTMLでは本来なら不整合なパスが生成されてしまいます。
1.3までは効率的でない方法によってこのパスの問題を解決していましたが、直近のバージョンでは自動的に解決することができるようになりました。特に意識しなくてもテンプレートもコンテンツもパスを書くことができます。
複雑な応用 /example/jdp /example/play
その他のサンプルはexample/jdpやexample/playなどにありますのでいろいろと試してください。
原理と応用
framexsはXSLなのでXSLT処理を行わせるタイミングや方法としては
- ケース1 クライアントのアクセスごとにクライアント側のブラウザでXSLT処理を行わせ、同時に結果HTMLを処理させる。
- ケース2 クライアントのアクセスごとにサーバー側でXSLT処理を行い、クライアントに対して結果HTMLをサーバーが出力する。
- ケース3 Static Site Generatorとしてコンテンツに対して任意のプログラムにXSLT処理を行わせ結果HTMLを作っておき、クライアントからのアクセスではそれらのファイルを直接出力する。
などがあります。今回はケース1のみの解説ですが、他のケースで活用することは可能なはずです。
現状とこれから
現在のframexsのバージョンは1.5.2になっています。1.3まではサンプルも含めてframexsを創る過程ではほぼ一人でやってきました。コードを書きサンプルを作りミスがないかテストするのはすべて私一人で行ってきました。しかし、これは限界に達してしまい、これ以上独力で次バージョンを作っていくのは無理な状況でした。しかし、別階層のパスの問題を解決するコードを提供され無事バージョンを上げることができました。ソーシャルコーディングの恩恵がどれほど大きいのかを初めて知ることができました。
そして、このバージョンをもってframexsのバージョン1系の開発は終了しました。XSLT1.0でできることはやりつくしたと判断したためです。XSLT2.0で書くバージョン2系のframexsも頭にありますが、開発予定は未定です。
終わりに
どうだったでしょうか。XSLTの可能性について理解がすすんだでしょうか。今回解説した方法はXSLT1.0に基づいています。もしブラウザーがより機能の高いXSLT2.0、もしくはXSLT3.0に対応したならば――少なくとも開発者にとって――世界は大きく変わるでしょう。
@nandaka_furariでツイッターもやっていますのでこの記事で解らない所があるといった事や間違いの指摘などはお気軽にご連絡ください。