前回はGoogleAppsScriptでRSS2.0フィードを作成・出力してみました。
そのフィードが問題ないかをチェックするW3CのFeed Validation Serviceを通してみたところ、いくつかRecommendationsが出てきたので対応してみることにしました。
W3C Feed Validation Service, for Atom and RSS
当初のRecommendations
item要素の不足
line 13, column 4: item should contain a guid element (2 occurrences) </item> ^
guid要素が不足していたようなので、設定するようにしました。
channel要素の不足
line 20, column 2: Missing atom:link with rel="self" </channel> ^
サーバーエラーが発生しました。しばらくしてからもう一度試してください。
XmlService.createElement('atom:link') では問題なく動作したことから、コロンをうまく扱ってくれていないようでした。
他の方法として
- ユニコード文字列('atom\u003Alink')を与えても、エラー
- jQuery対応で挙げられていた、 '\\:' としても、エラー
を試してみてもうまくいかなかったため、仕方なく最後に文字列化したところでreplaceをするようにしました。
次のRecommendations
上記の対応後に再度チェックしてみたところ、Recommendationsが変わっていました。
line 4, column 4: XML parsing error: <unknown>:4:4: unbound prefix <atom:link href="http://example.com" rel="self" type="application/rss+xm ... ^
xmlのネームスペースの定義をしていなかったようなので、追加しました。
参考:PubSubHubBubのpubに対応する : ryo.com
最後のRecommendations
再度チェックしてみたところ、またRecommendationsが変わっていました。
line 4, column 81: Self reference doesn't match document location ... rel="self" type="application/rss+xml" />
以下のstackoverflowの対応もしてみましたが、Recommendationsは変わりませんでした。
参考:rss2 - RSS atom:link - Self reference doesn't match document location - Stack Overflow
Feed Validation Serviceのヘルプも見ましたが、「This may not be a problem」との記載もあったため、今回はこれ以上追求するのをやめておくことにしました。
参考:Self reference doesn't match document location
その他
前回のコードでは、addTitle()するとすぐにRSSのタグを生成していましたが、後からタグの内容を修正するのが面倒そうだったので、最後で生成するように変更しました。
また、addItem()の引数が多かったのですが、引数の意味や順番が分かりにくかったため、名前付き引数へと変更しました。
ソース
Gistもアップデートしておきました。
GoogleAppsScriptでRSS2.0フィードを出力する例 (使う時は、.jsを.gsにする)
example.gs
function doGet() { var rss = makeRss(); rss.setTitle('RSS 2.0 test'); rss.setLink('http://example.com'); rss.setDescription('RSS 2.0のテスト'); rss.setLanguage('ja'); rss.setAtomlink('http://example.com/rss'); for (var i = 1; i < 3; i++){ rss.addItem({title: 'ページ:' + i, link: 'http://example.com/' + i, description: i + 'つ目のページ', pubDate: new Date() }) } return ContentService.createTextOutput(rss.toString()) .setMimeType(ContentService.MimeType.RSS); }
rss20feed.gs
var makeRss = function(){ var channel = XmlService.createElement('channel'); var root = XmlService.createElement('rss') .setAttribute('version', '2.0') .setAttribute('xmlnsatom', "http://www.w3.org/2005/Atom") .addContent(channel); var title = ''; var link = ''; var description = ''; var language = ''; var atomlink = ''; var items = {}; var createElement = function(element, text){ return XmlService.createElement(element).setText(text); }; return { setTitle: function(value){ title = value; }, setLink: function(value){ link = value; }, setDescription: function(value){ description = value; }, setLanguage: function(value){ language = value; }, setAtomlink: function(value){ atomlink = value; }, addItem: function(args){ if (typeof args.title === 'undefined') { args.title = ''; } if (typeof args.link === 'undefined') { args.link = ''; } if (typeof args.description === 'undefined') { args.description = ''; } if (!(args.pubDate instanceof Date)) { throw 'pubDateは日付型'; } if (typeof args.timezone === 'undefined') { args.timezone = "JST"; } if (typeof args.guid === 'undefined' && typeof args.link === 'undefined') { throw 'GUIDが設定できません'; } var item = { title: args.title, link: args.link, description: args.description, pubDate: Utilities.formatDate(args.pubDate, args.timezone, "EEE, dd MMM yyyy HH:mm:ss Z"), guid: args.guid === 'undefined' ? args.link : args.link } items[item.guid] = item; }, toString: function(){ // createElementではコロンの指定ができないため、あとでreplaceする。 // 誤って他のデータをreplaceしないよう、先頭に変換対象のatomlinkを持ってきておく channel.addContent(XmlService.createElement("atomlink") .setAttribute('href', atomlink) .setAttribute('rel', 'self') .setAttribute('type', 'application/rss+xml') ); channel.addContent(createElement('title', title)); channel.addContent(createElement('link', link)); channel.addContent(createElement('description', description)); channel.addContent(createElement('language', language)); for (var i in items) { channel.addContent( XmlService .createElement('item') .addContent(createElement('title', items[i].title)) .addContent(createElement('link', items[i].link)) .addContent(createElement('description', items[i].description)) .addContent(createElement('pubDate', items[i].pubDate)) .addContent(createElement('guid', items[i].guid)) ); } var document = XmlService.createDocument(root); var xml = XmlService.getPrettyFormat().format(document) // コロンが必要なタグの変換作業は、xml化できたところで行う var result = xml.replace('xmlnsatom', 'xmlns:atom') .replace('<atomlink href=','<atom:link href='); return result; } }; };