blog/content/post/2010/04/26/2010-04-26-00001356.md

189 lines
11 KiB
Markdown
Raw Normal View History

2019-03-31 11:00:21 +00:00
---
title: SXML から 任意の要素とかタグの値を取得する関数
author: kazu634
date: 2010-04-26
wordtwit_post_info:
- 'O:8:"stdClass":13:{s:6:"manual";b:0;s:11:"tweet_times";i:1;s:5:"delay";i:0;s:7:"enabled";i:1;s:10:"separation";s:2:"60";s:7:"version";s:3:"3.7";s:14:"tweet_template";b:0;s:6:"status";i:2;s:6:"result";a:0:{}s:13:"tweet_counter";i:2;s:13:"tweet_log_ids";a:1:{i:0;i:5241;}s:9:"hash_tags";a:0:{}s:8:"accounts";a:1:{i:0;s:7:"kazu634";}}'
categories:
- gauche
- Lisp
---
<div class="section">
<p>
Tumblr の Web サービスから XML を取得して、ごにょごにょする準備のため、作成してみました。
</p>
<h4>
読み込む SXML ファイル
</h4>
<p>
読み込むSXMLはこんな感じです:
</p>
<pre class="syntax-highlight">
(post
(|@|
(url-with-slug &#34;http://kazu634.tumblr.com/post/547214365&#34;)
(url &#34;http://kazu634.tumblr.com/post/547214365&#34;)
(unix-timestamp &#34;1272170551&#34;)
(type &#34;quote&#34;)
(slug &#34;&#34;)
(reblog-key &#34;Yp9UyNJ1&#34;)
(id &#34;547214365&#34;)
(format &#34;html&#34;)
(date-gmt &#34;2010-04-25 04:42:31 GMT&#34;)
(date &#34;Sun, 25 Apr 2010 13:42:31&#34;))
(quote-text &#34;情報が便利になればなるほどに、お金はそこから逃げていく。裏を返せば、情報という物に、何かの不自由を付加したものがメディアであって、不自由を付加されて、初めてその情報は、お金に紐付けられるのだと思う。&#34;)
(quote-source &#34;<span class="synIdentifier">&#60;a </span><span class="synType">href</span>=<span class="synIdentifier">\</span><span class="synConstant">&#34;http://medt00lz.s59.xrea.com/wp/archives/787\&#34;</span><span class="synIdentifier"> </span><span class="synType">target</span>=<span class="synIdentifier">\</span><span class="synConstant">&#34;_blank\&#34;</span><span class="synIdentifier">&#62;</span>不便が強みになる - レジデント初期研修用資料<span class="synIdentifier">&#60;/a&#62;</span>&#34;))
</pre>
<h4>
作成した関数
</h4>
<p>
作成したのはこんな関数:
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>get-value-from-sxml key lis<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cond</span>
[<span class="synSpecial">(</span><span class="synStatement">null</span>? lis<span class="synSpecial">)</span> #f]
[<span class="synSpecial">(</span><span class="synStatement">atom</span>? <span class="synSpecial">(</span><span class="synStatement">car</span> lis<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">equal</span>? <span class="synSpecial">(</span><span class="synStatement">car</span> lis<span class="synSpecial">)</span> key<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cadr</span> lis<span class="synSpecial">)</span>
<span class="synSpecial">(</span>get-value-from-sxml key <span class="synSpecial">(</span><span class="synStatement">cdr</span> lis<span class="synSpecial">)))</span>]
[else
<span class="synSpecial">(</span><span class="synStatement">or</span>
<span class="synSpecial">(</span>get-value-from-sxml key <span class="synSpecial">(</span><span class="synStatement">car</span> lis<span class="synSpecial">))</span>
<span class="synSpecial">(</span>get-value-from-sxml key <span class="synSpecial">(</span><span class="synStatement">cdr</span> lis<span class="synSpecial">)))</span>]<span class="synSpecial">))</span>
</pre>
<h4>
2010/04/27 追記
</h4>
<p>
Shiroさんからコメント頂きました:
</p>
<blockquote>
<p>
ドキュメントが少なくてアレなんですが、SXMLから要素を抜き出すのはsxpathが便利ですよ。
</p>
<p>
上の(post (@ &#8230;)) なるsxmlが変数*sxml*に入っているとして、
</p>
<p>
type要素の値の抜きだし:
</p>
<p>
<span class="footnote"><a href="/sirocco634/#f1" name="fn1" title="sxpath &#39;(// type *text*">*1</a></span> *sxml*) => (&#8220;quote&#8221;)
</p>
<p>
quote-text要素だと:
</p>
<p>
((sxpath &#8216;(// quote-text *text*) *sxml*) => (&#8220;情報が便利になればなるほどに、 &#8230;
</p>
<p>
戻り値がリストなのは複数マッチする可能性があるからですが、マッチした最初の
</p>
<p>
値が欲しければif-car-sxpathを使います (マッチしなければ#fが返る)
</p>
<p>
<span class="footnote"><a href="/sirocco634/#f2" name="fn2" title="if-car-sxpath &#39;(// unix-timestamp *text*">*2</a></span> *sxml*) => &#8220;1272170551&#8221;
</p>
</blockquote>
<p>
実は Tumblr から取得した XML を SXML に変換する過程ですでに sxpath を使っていました。こんな感じです。
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>test-sxpath sxpath<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let*</span>
<span class="synSpecial">((</span>xml
<span class="synSpecial">(</span>open-input-file
<span class="synConstant">&#34;/Users/kazu634/Documents/working/tmp_lisp/tumblr/tumblr.xml&#34;</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>sxml <span class="synSpecial">(</span>ssax:xml-&#62;sxml xml <span class="synSpecial">'())))</span>
<span class="synSpecial">((</span>sxpath sxpath sxml<span class="synSpecial">)))</span> <span class="synComment">;; この部分に冒頭の関数を使うことを考えてた</span>
</pre>
<p>
でもこれだと sxpath の部分に「'(// unix-timestamp)」などと指定しなければならずあんまり嬉しくなくて、自分でリストをごりごりやった方がいいのではないかと思って冒頭の関数を書きました。でも、 Shiro さんのコメントを見ていて、こんな風に書き換えればいいのではないかと思いつきました:
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>test-sxpath element-name<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let*</span>
<span class="synSpecial">((</span>xml
<span class="synSpecial">(</span>open-input-file
<span class="synConstant">&#34;/Users/kazu634/Documents/working/tmp_lisp/tumblr/tumblr.xml&#34;</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>sxml <span class="synSpecial">(</span>ssax:xml-&#62;sxml xml <span class="synSpecial">'())))</span>
<span class="synSpecial">((</span>sxpath <span class="synPreProc">`(</span><span class="synStatement">//</span> ,element-name<span class="synPreProc">)</span><span class="synSpecial">)</span> sxml<span class="synSpecial">)))</span>
</pre>
<p>
これなら任意の要素の値とか属性の値を直感的にピンポイントで取得できます!
</p>
<p>
実行例はこんな感じになりました:
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span><span class="synStatement">car</span> <span class="synSpecial">(</span>test-sxpath <span class="synSpecial">'</span><span class="synIdentifier">post</span><span class="synSpecial">))</span> <span class="synComment">;; tumblr.xmlには複数の post要素があるのでとりあえず1つめを指定</span>
<span class="synSpecial">(</span>post
<span class="synSpecial">(|@|</span>
<span class="synSpecial">(</span>url-with-slug <span class="synConstant">&#34;http://kazu634.tumblr.com/post/547214365&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>url <span class="synConstant">&#34;http://kazu634.tumblr.com/post/547214365&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>unix-timestamp <span class="synConstant">&#34;1272170551&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">type</span> <span class="synConstant">&#34;quote&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>slug <span class="synConstant">&#34;&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>reblog-key <span class="synConstant">&#34;Yp9UyNJ1&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>id <span class="synConstant">&#34;547214365&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">format</span> <span class="synConstant">&#34;html&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>date-gmt <span class="synConstant">&#34;2010-04-25 04:42:31 GMT&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>date <span class="synConstant">&#34;Sun, 25 Apr 2010 13:42:31&#34;</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>quote-text <span class="synConstant">&#34;情報が便利になればなるほどに、お金はそこから逃げていく。裏を返せば、情報という物に、何かの不自由を付加したものがメディアであって、不自由を付加されて、初めてその情報は、お金に紐付けられるのだと思う。&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>quote-source <span class="synConstant">&#34;&#60;a href=\&#34;http://medt00lz.s59.xrea.com/wp/archives/787\&#34; target=\&#34;_blank\&#34;&#62;不便が強みになる - レジデント初期研修用資料&#60;/a&#62;&#34;</span><span class="synSpecial">))</span>
</pre>
<p>
sxpathを使って最初にヒットしたものを取得する方法も考えていたのですが、 if-car-sxpath だったんですね。同じように関数をつくってみました:
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span>define <span class="synSpecial">(</span>get-value-from-sxml2 sxpath sxml<span class="synSpecial">)</span>
<span class="synSpecial">((</span>if-car-sxpath <span class="synPreProc">`(</span><span class="synStatement">//</span> ,sxpath <span class="synType">*text*</span><span class="synPreProc">)</span><span class="synSpecial">)</span> sxml<span class="synSpecial">))</span>
gosh&#62; <span class="synSpecial">(</span>get-value-from-sxml <span class="synSpecial">'</span><span class="synIdentifier">unix-timestamp</span> <span class="synSpecial">(</span><span class="synStatement">car</span> <span class="synSpecial">(</span>test-sxpath <span class="synSpecial">'</span><span class="synIdentifier">post</span><span class="synSpecial">)))</span>
<span class="synConstant">&#34;1272170551&#34;</span>
</pre>
<p>
だいぶ見通しが良くなりました!ありがとうございます!
</p>
</div>
<div class="footnote">
<p class="footnote">
<a href="/sirocco634/#fn1" name="f1">*1</a>sxpath '(// type *text*
</p>
<p class="footnote">
<a href="/sirocco634/#fn2" name="f2">*2</a>if-car-sxpath '(// unix-timestamp *text*
</p>
</div>