HTML
XML
RSS
XSLT

atomフィードをXSLTで加工してiframeで埋める

screenshot 2018-05-10 10.04.52.png

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ファイル内部に次のdoctype宣言を書けば良いです。

<?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分くらい悩みました。

結果的にできたもの

screenshot 2018-05-10 10.04.52.png

出来たxmlファイルをブラウザで表示するとhtmlファイルのように表示され、iframeで他のhtmlファイルに埋め込んでも正常に動きました。

デモ: https://set0gut1.github.io/atom-xslt-example/
全ソース: https://github.com/set0gut1/atom-xslt-example