2009年1月29日木曜日

XMLを扱う (その9) [Common Lisp]


Common LispでのXMLの取り扱いを検討するワークフロー
--------------------------------------------------

CLでのXMLの取り扱いをしらべる。まず、実践CLにあるように、S式から
XHTML(XML)に変換するインタプリタやコンパイラを作るというやり方はあ
る。ただし、要素や属性についていちいち手で追加するのか、Schemaから
自動抽出にするのかということがある。

次にライブラリでどういうものがあるかを調べる。

まずclbuildに何があるか。

---
cxml get_git git://repo.or.cz/cxml.git #XML parser and DOM implementation
cxml-stp get_lichteblau_com #STP, a DOM alternative
---

この2つの概要を調べる。
まずcxml。

http://www.cliki.net/CXML

** XML parser
** 名前空間対応
** 妥当性検証対応(対応しているSchemaは?)
** In-memory表現
*** DOM
*** xmls形式
*** cxml-stp (あ、これはcxmlのadd-onなのね)
** cxml-rngというadd onにてRelax NGに対応。
** plexippus-xpathにてXPath対応。
** HTMLにはclosure-htmlで対応。
** Closure web browserに利用されている。

というわけで、cxml-stpとは比較対象ではなく、同一の選択肢であった。

知らないことを確認する。

xmls形式って何?

http://www.cliki.net/XMLS
http://common-lisp.net/project/xmls/

---
<?xml version="1.0"?>
<!-- test document -->
<book title='The Cyberiad'>
<!-- comment in here -->
<author xmlns='http://authors'>Stanislaw Lem</author>
<info:subject xmlns:info='http://bookinfo' rank='1'>"Cybernetic Fables"</info:subject>
</book>
---

これが、

---
("book" (("title" "The Cyberiad"))
(("author" . "http://authors") NIL "Stanislaw Lem")
(("subject" . "http://bookinfo") (("rank" "1")) "\"Cybernetic Fables\""))
---

これになる、と。

ルールは、次のようだ。

** 要素名 -> 文字列
** 名前空間URI -> 文字列
** 要素(名前空間なし) -> リスト (要素名 属性リスト 子要素*)
** 要素(名前空間あり) -> リスト ((要素名 . 名前空間) 属性リスト 子要素)
** 属性名 -> 文字列
** 属性(名前空間なし) -> リスト ((属性名 属性値) ...)
** 属性(名前空間あり) -> これがわからない。

試す。

(asdf:oos 'asdf:load-op 'xmls)

---
CL-USER(12): (xmls:parse "<book xmlns='http://n1'><author att='piyo'>test</author></book>")
(("book" . "http://n1") NIL (("author" . "http://n1") (("att" "piyo")) "test"))
CL-USER(13): (xmls:parse "<book xmlns='http://n1' xmlns:n2='http://n2'><author att='piyo'>test</author></book>")
(("book" . "http://n1") NIL (("author" . "http://n1") (("att" "piyo")) "test"))
CL-USER(14): (xmls:parse "<book xmlns='http://n1' xmlns:n2='http://n2'><n2:author att='piyo'>test</author></book>")
(("book" . "http://n1") NIL (("author" . "http://n2") (("att" "piyo")) "test"))
CL-USER(15): (xmls:parse "<book xmlns='http://n1' xmlns:n2='http://n2'><author n2:att='piyo'>test</author></book>")
(("book" . "http://n1") NIL (("author" . "http://n1") (("att" "piyo")) "test"))
CL-USER(20): (xmls:parse "<x xmlns:edi='http://ecommerce.example.org/schema'><lineItem edi:taxClass='exempt'>Baby food</lineItem></x>")
("x" NIL ("lineItem" (("taxClass" "exempt")) "Baby food"))
---

なるほど。属性については名前空間に対応していないのだな。これは困る
だろう。

さて、cxmlを使ってみる。

(asdf:oos 'asdf:load-op 'cxml)

使いかた情報はここにあるようだ。

http://common-lisp.net/project/cxml/

Quick-Startにしたがって多少いじってみる。
---
CL-USER(27): (with-open-file (s "example.xml" :direction :output)
(write-string "<test a='b'><child/></test>" s))
"<test a='b'><child/></test>"
CL-USER(28): (cxml:parse-file "example.xml" (rune-dom:make-dom-builder))
#<RUNE-DOM::DOCUMENT @ #x10014c8292>
CL-USER(29): (defparameter *example* *)
*EXAMPLE*
CL-USER(30): *
*EXAMPLE*
CL-USER(31): *example*
#<RUNE-DOM::DOCUMENT @ #x10014c8292>
CL-USER(32): (dom:document-element *example*)
#<RUNE-DOM::ELEMENT test @ #x1000e185f2>
CL-USER(33): (dom:tag-name *)
"test"
CL-USER(34): (with-open-file (out "example.out" :direction :output :element-type '(unsigned-byte 8))
(dom:map-document (cxml:make-octet-stream-sink out) *example*))
#<EXCL::BINARY-OUTPUT-FILE-STREAM #P"example.out" closed @ #x1001407c12>
CL-USER(35): (cxml:parse-file "example.xml" (cxml-xmls:make-xmls-builder))
("test" (("a" "b")) ("child" NIL))
CL-USER(36): (cxml:parse-file "example-ns.xml" (cxml-xmls:make-xmls-builder))
("x" ((("edi" . "http://www.w3.org/2000/xmlns/") "http://ecommerce.example.org/schema")) "
" ("lineItem" ((("taxClass" . "http://ecommerce.example.org/schema") "exempt")) "Baby food") "
")
CL-USER(37):
---

ここでexample-ns.xmlの中身は次のもの。

---
<x xmlns:edi='http://ecommerce.example.org/schema'>
<lineItem edi:taxClass='exempt'>Baby food</lineItem>
</x>
---

cxmlのxmls-likeというのは属性の名前空間にも対応している。


この項、つづく。

0 件のコメント: