Common Lisp からブログに投稿するまで [Lisp]
So-net blog が XML-RPC からの投稿に対応したということで Common Lisp から投稿してみることにしました。
* S-XML-RPC の導入
Common Lisp から XML-RPC を使うためのパッケージとしては S-XML-RPC [1] というのがあるようです。これは S-XML [2] に依存するそうなのでこれも必要です。
Common Lisp で外部ライブラリを使うにはどうするかということがそもそも分からなかったのですが Common Lisp には ASDF という Debian の apt-get みたいな機構があってそれを使えばいいようです。
もともと Windows マシンに入っていた GCL で ASDF を使う方法が分からなかったのでまずは SBCL [3] の Windows 版を入れることにしました。
asdf-install というのを使うと本当に apt-get みたいにネットワークからのダウンロードからやってくれるらしいのですが、これは Windows 上だと tar を使えないといけなかったり、とにかく何かと面倒そうなので単純に S-XML と S-XML-RPC の tar ball を C:\Program Files\Steel Bank Common Lisp\1.0.13 の下に展開して、
(require :asdf) (require :s-xml) (require :s-xml-rpc)
としました。これでインストールは完了。
* S-XML-RPC を使う
So-net blog の XML-RPC のエントリポイントは http://blog.so-net.ne.jp/_rpc なので、とりえあず、
(in-package :s-xml-rpc) (setf *xml-rpc-host* "blog.so-net.ne.jp") (setf *xml-rpc-url* "/_rpc")
とします。
XML-RPC のリクエストの XML を作るには encode-xml-rpc-call を使います。
* (encode-xml-rpc-call "mt.supportedMethods") "<methodCall><methodName>mt.supportedMethods</methodName></methodCall>"
これを使ってリモート呼び出しをするには xml-rpc-call を使います。
* (xml-rpc-call (encode-xml-rpc-call "mt.supportedMethods")) ("blogger.newPost" "blogger.editPost" "blogger.getRecentPosts" "blogger.getUsersBlogs" "blogger.getUserInfo" "blogger.deletePost" "metaWeblog.getPost" "metaWeblog.newPost" "metaWeblog.editPost" "metaWeblog.getRecentPosts" "metaWeblog.newMediaObject" "mt.getCategoryList" "mt.setPostCategories" "mt.getPostCategories" "mt.supportedTextFilters" "mt.getRecentPostTitles" "mt.publishPost")
ところで encode-xml-rpc-call にバグを見つけました(2004-06-17版)。
* (encode-xml-rpc-call "blogger.newPost" "" "rainyday" "rainyday" "password" "test" nil) "<methodCall><methodName>blogger.newPost</methodName><params><param><value><stri ng></string></value></param><param><value><string>rainyday</string></value></par am><param><value><string>rainyday</string></value></param><param><value><string> password</string></value></param><param><value><string>test</string></value></pa ram><param><value><string>NIL</string></value></param></params></methodCall>"
nil は <string>NIL</string> ではなくて <boolean>0</boolean> に変換されて欲しいんですが、文字列になってしまっています。
原因究明のためにソースを見ると
(defun encode-xml-rpc-value (arg stream)
(princ "<value>" stream)
(cond ((or (stringp arg) (symbolp arg))
(princ "<string>" stream)
(print-string-xml (string arg) stream)
(princ "</string>" stream))
((integerp arg) (format stream "<int>~d</int>" arg))
((floatp arg) (format stream "<double>~f</double>" arg))
((or (null arg) (eq arg t))
(princ "<boolean>" stream)
(princ (if arg 1 0) stream)
(princ "</boolean>" stream))
nil も t も symbolp に対して真なので boolean の条件節まで辿り着かないみたいですね。というわけで修正して順序を変えました。
(defun encode-xml-rpc-value (arg stream)
(princ "<value>" stream)
(cond
((or (null arg) (eq arg t))
(princ "<boolean>" stream)
(princ (if arg 1 0) stream)
(princ "</boolean>" stream))
((or (stringp arg) (symbolp arg))
(princ "<string>" stream)
(print-string-xml (string arg) stream)
(princ "</string>" stream))
さて、ブログへの投稿には blogger.newPost ではなくて metaWeblog.newPost を使うことにしました。
(xml-rpc-call
(encode-xml-rpc-call "metaWeblog.newPost"
"rainyday" "rainyday" "password"
(xml-rpc-struct
"title" "テスト"
"description" "<p>本文1</p><p>本文2</p>"
"convert_breaks" nil)
nil))
投稿内容の構造体を xml-rpc-struct で作っています。これは (xml-rpc-struct :title "テスト" ... のような感じでいいかと思っていたのですが、そうすると XML 上では小文字の title ではなくて大文字の TITLE になってしまうのでアウトでした。
これで投稿に成功すると記事のIDが帰ってきます。
本文は S-XML で書くことにするというのも手かと思ったのですが、それだと日本語が Unicode の実体参照になってしまって不便でした。
他にも XML-RPC のリプライに日本語が含まれると SBCL が「This is probably a bug in SBCL itself.」とかいってエラーになるなどなかなか日本語の扱いは深追いしたくない感があるようです。
* 未整理の事柄
metaWeblog.newPost の最後の nil は記事を公開するかしないかのフラグのはずなのですが、So-net blog はこれを見ていない(?)みたいで関係なく公開になってしまうようです。あと第1引数の blogid と第2引数の username の使い分けもいまいち不明です。カテゴリを指定する方法も良く分からない。
[1] http://common-lisp.net/project/s-xml-rpc/
[2] http://common-lisp.net/project/s-xml/
コメント 0