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

![screenshot 2018-05-10 10.04.52.png](https://qiita-image-store.s3.amazonaws.com/0/235355/54186cc7-ca85-5c0b-cb12-85356188bd47.png)

[teratail「ホームページの新着情報部分だけをxmlで書いて読み込みたい」](https://teratail.com/questions/125287)でディスカッションしてたんですが、そこで次の欲求が湧いてきました。 

- 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文書にする](https://qiita.com/TongariAtamago/items/b19b57cd0678da471eeb)

# 最小構成のダミーatomフィード

最小限のデータのみ持ったatomフィードを作りたいです。
必須要素は id, title, updated の3つですが、更新履歴として記事の概要を付けたいので entry には summary も付けることにします。
最終的に以下のようなatomフィードを用意しました。

```xml
<?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の仕様をまとめる](https://qiita.com/you8/items/e903fd463cf770688e1e)
参考記事: [最小限のAtomフォーマット](https://qiita.com/Hiraku/items/a191e5f1a90eb7b4aad1)

# atom.xmlをインクルードする別のxmlファイルを作る

XSLTの定義ファイルが`foobar.xsl`だとして、xmlファイルにXSLTを適用した結果をブラウザに表示するには、xmlファイル内部に次の処理命令を書けば良いです。

```xml
<?xml-stylesheet type="text/xsl" href="foobar.xsl"?>
```

ただしatom.xmlなどのフィードは自動生成されることも多く、「atom.xmlをて編集して一行付け足す」という手順は良手とは思えません。
なんかいい方法ないかなーと探してみたところ [StackOverflow: Apply XSL to External XML](https://stackoverflow.com/questions/1483759/apply-xsl-to-external-xml/) に良さげな方法を発見しました。
曰く、次のようにすればatom.xmlをインクルードする別の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
<?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ファイルの方でマッチさせるときも同じネームスペースでマッチするようprefix`a:`を定義する必要があるということです。
~~これで30分くらい悩みました。~~

# 結果的にできたもの

![screenshot 2018-05-10 10.04.52.png](https://qiita-image-store.s3.amazonaws.com/0/235355/54186cc7-ca85-5c0b-cb12-85356188bd47.png)

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

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