teratail「ホームページの新着情報部分だけをxmlで書いて読み込みたい」でディスカッションしてたんですが、そこで次の欲求が湧いてきました。
- atomフィードやRSSフィードと同じ内容を新着情報としてHTMLに埋め込みたい
- ただし、サーバーサイドのコードを書いてHTMLを生成するのは面倒くさい
- jqueryのajaxとかでatomの内容を取得・加工してDOMに吊るすのも少し面倒くさい
XSLTを使えばxmlの内容をブラウザ側でhtmlに変換でき、それをiframeで埋めれば楽に実現できるのではないかと思い、実際にやってみたので書き留めます。
XSLTとは
XSLTとはXML文書を別のフォーマットのXML文書に変換するための言語です。
eXtensible Stylesheet Language Transformationsの略らしいです。
今回はこれをXMLからHTMLに翻訳するために仕様します。
参考記事: XMLをXSLTを使ってHTML文書にする
最小構成のダミーatomフィード
最小限のデータのみ持ったatomフィードを作りたいです。
必須要素は id, title, updated の3つですが、更新履歴として記事の概要を付けたいので entry には summary も付けることにします。
最終的に以下のようなatomフィードを用意しました。
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>http://example.com/</id>
<title>sample latest updates</title>
<updated>2018-05-09T00:00:00+09:00</updated>
<entry>
<id>http://example.com/post6.html</id>
<title>Title of article 6</title>
<updated>2018-05-12T00:00:00+09:00</updated>
<summary>Summary of article 6</summary>
</entry>
<entry>
<id>http://example.com/post5.html</id>
<title>Title of article 5</title>
<updated>2018-05-11T00:00:00+09:00</updated>
<summary>Summary of article 5</summary>
</entry>
<!-- 中略 -->
</feed>
参考記事: RSS、atomの仕様をまとめる
参考記事: 最小限のAtomフォーマット
atom.xmlをインクルードする別のxmlファイルを作る
XSLTの定義ファイルがfoobar.xsl
だとして、xmlファイルにXSLTを適用した結果をブラウザに表示するには、xmlファイル内部に次の処理命令を書けば良いです。
<?xml-stylesheet type="text/xsl" href="foobar.xsl"?>
ただしatom.xmlなどのフィードは自動生成されることも多く、「atom.xmlをて編集して一行付け足す」という手順は良手とは思えません。
なんかいい方法ないかなーと探してみたところ StackOverflow: Apply XSL to External XML に良さげな方法を発見しました。
曰く、次のようにすればatom.xmlをインクルードする別のxmlファイルを作れるそうです。
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="wrapper.xsl"?>
<!DOCTYPE wrapper [
<!ENTITY content SYSTEM "atom.xml">
]>
<wrapper>
&content;
</wrapper>
これでatom.xmlは一切変更することなく、その内容にXSLTを適用することが可能になりました。やったぜ!
変換規則の定義
XSLTの内容は次のようにしました。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://www.w3.org/2005/Atom">
<xsl:output method="html" encoding="UTF-8"/>
<xsl:template match="text()"/>
<xsl:template match="a:feed">
<html>
<head>
<title>
<xsl:value-of select="a:title"/>
</title>
<link rel="stylesheet" href="wrapper.css"/>
</head>
<body>
<h1>
<xsl:value-of select="a:title"/>
</h1>
<ul>
<xsl:apply-templates/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="a:entry">
<li>
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="a:id"/>
</xsl:attribute>
<xsl:value-of select="a:title"/>
</xsl:element>
:
<xsl:value-of select="a:summary"/>
(<xsl:value-of select="substring(a:updated, 1, 10)"/>)
</li>
</xsl:template>
</xsl:stylesheet>
注意点としては、atom.xmlの方でhttp://www.w3.org/2005/Atom
ネームスペースで書いているので、xslファイルの方でマッチさせるときも同じネームスペースでマッチするようprefixa:
を定義する必要があるということです。
これで30分くらい悩みました。
結果的にできたもの
出来たxmlファイルをブラウザで表示するとhtmlファイルのように表示され、iframeで他のhtmlファイルに埋め込んでも正常に動きました。
デモ: https://set0gut1.github.io/atom-xslt-example/
全ソース: https://github.com/set0gut1/atom-xslt-example