%include "default.mgp"
%%default 1 left, size 6, fore "light yellow", back "blue5", font "standard", ccolor "white", vgap 35
%%default 2 noop
%%default 3 fore "white", bar "gray70", vgap 10
%%default 4 noop
%%tab 1 noop
%%tab 2 noop
%%tab 3 noop
%default 1 left, size 6, fore "light yellow", back "blue4", font "standard", ccolor "white", vgap 35, bgrad 0 0 128 45 1 "#000080" "#400040" "#800000"
%default 2 size 5
%default 3 size 2, fore "white", bar "gray70", vgap 10
%default 4 size 4
%tab 1 size 4
%tab 2 size 3
%tab 3 size 3
%page
%fore "white", size 9, vgap 15
%center, font "thick"
%ccolor "white"
%size 10
Ruby と XML
%size 6, fore "orange", font "standard"
吉田正人
%size 5, fore "green"
ドコモ・システムズ (株)
%font "typewriter", size 4, fore "yellow"
yoshidam@yoshidam.net
%font "typewriter", size 4, fore "yellow"
yoshidam@docomo-sys.co.jp
%page
目次
%leftfill
XML プログラミングの概要
XMLParser ライブラリの実装について
DOM ライブラリの実装について
XMLParser ライブラリの使い方
DOM ライブラリの使い方
もう少し実用的なプログラムの例
今後について
付録
%page
XML プログラミングの概要 (1)
%leftfill
XML プログラミングのための API
イベント形式
SAX (Simple API for XML)
PYX (ESIS)
オブジェクト形式
DOM (Document Object Model)
%page
XML プログラミングの概要 (1-1)
%leftfill
SAX (http://www.megginson.com/SAX/):
イベント (要素開始, 終了, 文字データ, 処理命令など) に対するハンドラを定義する
ほとんどの Java の XML パーサで対応している
%font "typewriter", size 3, fore "yellow"
SAX API の一部 (Java)
public interface DocumentHandler {
public abstract void startDocument();
public abstract void endDocument();
public abstract void startElement(String name, AttributeList atts);
public abstract void endElement(String name);
public abstract void characters(char ch[], int start, int length);
public abstract void ignorableWhitespace(char ch[], int start, int length);
public abstract void processingInstruction(String target, String data);
}
%page
XML プログラミングの概要 (1-2)
%leftfill
PYX (http://www.xml.com/pub/a/2000/03/15/feature/index.html):
イベントを行指向の記法で出力する
SGML のプログラミングでよく使われていた
%font "typewriter", size 3, fore "yellow"
XML ファイル PYX 記法
(test
Aattr1 test
ほげ → -\n
(hoge
-\n ほげ\n
)hoge
-\n
)test
%page
XML プログラミングの概要 (1-3)
%leftfill
DOM (http://www.w3.org/TR/REC-DOM-Level-1):
IDL で定義されたオブジェクトモデル
W3C 標準
%font "typewriter", size 3, fore "yellow"
DOM のインターフェイスの例 (OMG IDL)
interface Node {
readonly attribute DOMString nodeName;
attribute DOMString nodeValue;
readonly attribute unsigned short nodeType;
readonly attribute Node parentNode;
readonly attribute NodeList childNodes;
readonly attribute Node firstChild;
readonly attribute Node lastChild;
readonly attribute Node previousSibling;
readonly attribute Node nextSibling;
Node insertBefore(in Node newChild,
in Node refChild)
raises(DOMException);
Node appendChild(in Node newChild)
raises(DOMException);
};
%page
XML プログラミングの概要 (2)
%leftfill
Ruby の利点
お手軽
コンパイル不要
変数宣言不要
メモリ管理不要
強力な文字列処理能力
Perl と同等の正規表現
日本語,UTF-8 の処理が可能
読みやすい,書きやすい
%page
XMLParser ライブラリの実装について (1)
%leftfill
expat とは
SP の作者である James Clark 氏が作った C 言語による高速 XML パーサライブラリ (http://www.jclark.com/xml/expat.html)
現在はバージョン 2.0 が expat プロジェクト (http://sourceforge.net/projects/expat/) によって開発中
expat の特徴
高速
validating は行わない
イベント方式の API
アプリケーションやライブラリへの組込みが容易 (Mozilla, Perl, Python, Tcl, PHP3 / PHP4, w3c-libwww, Apache)
%page
XMLParser ライブラリの実装について (1-1)
%leftfill
expat API の例 (C 言語)
%font "typewriter", size 3, fore "yellow"
XML_Parser XMLPARSEAPI
XML_ParserCreate(const XML_Char *encoding);
int XMLPARSEAPI
XML_Parse(XML_Parser parser, const char *s, int len, int isFinal);
void XMLPARSEAPI
XML_SetElementHandler(XML_Parser parser,
XML_StartElementHandler start,
XML_EndElementHandler end);
void XMLPARSEAPI
XML_SetCharacterDataHandler(XML_Parser parser,
XML_CharacterDataHandler handler);
void XMLPARSEAPI
XML_SetCommentHandler(XML_Parser parser,
XML_CommentHandler handler);
%page
XMLParser ライブラリの実装について (2)
%leftfill
基本方針
できるだけ簡略に記述できること
expat の高速さを生かすこと
イベントハンドラ無しの場合は expat そのもののスピードになるように
expat のすべての機能が扱えること
ただし,Ruby では不要な機能は除く
%page
XMLParser ライブラリの実装について (3)
%leftfill
Parser API について
イベントハンドラ・インタフェイス
XML::Parser クラスにメソッド定義することによりイベントハンドラを定義する
メソッド定義しなかったイベントは処理しない
イベント駆動プログラム
イテレータ・インターフェイス
XMLParser#parse メソッドをイテレータとして使用する
ループ処理のように記述できる
イベント駆動プログラムに慣れていなくても使える
基本イベント以外はダミーメソッドの定義が必要
SAX インターフェイス
%page
XMLParser ライブラリの実装について (3-1)
%leftfill
イベントハンドラ・インターフェイスの実装
%font "typewriter", size 3, fore "yellow"
static void
myStartElementHandler(void *recv,
const XML_Char *name, const XML_Char **atts)
{
VALUE attrhash;
attrhash = rb_hash_new();
while (*atts) {
const char* key = *atts++;
const char* val = *atts++;
rb_hash_aset(attrhash, rb_str_new2((char*)key), rb_str_new2((char*)val));
}
rb_funcall((VALUE)recv, id_startElementHandler, 2,
rb_str_new2((char*)name), attrhash);
}
%page
XMLParser ライブラリの実装について (3-2)
%leftfill
イベントハンドラ・インターフェイスの実装 (続き)
%font "typewriter", size 3, fore "yellow"
id_startElementHandler = rb_intern("startElement");
id_endElementHandler = rb_intern("endElement");
if (rb_method_boundp(CLASS_OF(obj), id_startElementHandler, 0))
start = myStartElementHandler;
if (rb_method_boundp(CLASS_OF(obj), id_endElementHandler, 0))
end = myEndElementHandler;
if (start || end)
XML_SetElementHandler(parser->parser, start, end);
%page
XMLParser ライブラリの実装について (3-3)
%leftfill
イベントハンドラのメソッド名
%font "typewriter", size 3, fore "yellow"
メソッド名 イベント
-------------------------------------------------------
startElement | element start tag
endElement | element end tag
character | character data
processingInstruction | processing instruction
unparsedEntityDecl | unparsed entity declaration
notationDecl | notation declaration
externalEntityRef | external entity reference
comment | comment
startCdata | CDATA section start
endCdata | CDATA section end
startNamespaceDecl | Namespace declaration start
endNamespaceDecl | Namespace declaration end
startDoctypeDecl | DOCTYPE declaration start
endDoctypeDecl | DOCTYPE declaration end
notStandalone | document is not standalone
default | other data
defaultExpand | same as default
unknownEncoding | unknown character encoding
%page
XMLParser ライブラリの実装について (3-4)
%leftfill
イテレータ・インターフェイスの実装
%font "typewriter", size 3, fore "yellow"
static void
iterStartElementHandler(void *recv,
const XML_Char *name, const XML_Char **atts)
{
VALUE attrhash;
attrhash = rb_hash_new();
while (*atts) {
const char* key = *atts++;
const char* val = *atts++;
rb_hash_aset(attrhash, rb_str_new2((char*)key), rb_str_new2((char*)val));
}
rb_yield(rb_ary_new3(3, INT2FIX(XML_START_ELEM),
rb_str_new2((char*)name), attrhash));
}
%page
XMLParser ライブラリの実装について (3-5)
%leftfill
イテレータ・インターフェイスの実装 (続き)
%font "typewriter", size 3, fore "yellow"
parser->iterator = rb_iterator_p();
if (parser->iterator) {
XML_SetElementHandler(parser->parser,
iterStartElementHandler, iterEndElementHandler);
...
}
%page
XMLParser ライブラリの実装について (3-6)
%leftfill
イテレータブロックへの引数
%font "typewriter", size 3, fore "yellow"
第一引数 (イベントタイプ) 第二引数 第三引数
--------------------------------------------------------------
START_ELEM | element name | hash of attributes
END_ELEM | element name | nil
CDATA | nil | string
PI | PI name | string
UNPARSED_ENTITY_DECL | entity name | array
NOTATION_DECL | notation name | array
EXTERNAL_ENTITY_REF | entity names | array
COMMENT | nil | string
START_CDATA | nil | nil
END_CDATA | nil | nil
START_NAMESPACE_DECL | prefix | URI
END_NAMESPACE_DECL | prefix | nil
START_DOCTYPE_DECL | doctype name | nil
END_DOCTYPE_DECL | nil | nil
DEFAULT | nil | string
%page
XMLParser ライブラリの実装について (4)
%leftfill
文字エンコーディング対応
パース前にエンコーディング変換
ISO-2022-JP はこの方法でのみ対応可能
XML::Encoding クラス
expat の UnknownEncoding イベントでパーサにコンバータを登録
Ruby を使ったエンコーディングコンバータも可能
Perl エンコーディングマップの流用
Perl の XML::Parser モジュールの機能を流用
Perl の XML::Encoding モジュールを利用し,マッピングテーブル作成
EUC-JP,Shift_JIS 等が利用可能
%page
XMLParser ライブラリの実装について (4-1)
%leftfill
パース前にエンコーディング変換
%font "typewriter", size 3, fore "yellow"
jis = open("sample.xml").read
utf8 = Uconv.euctou8(NKF.nkf("-Je", jis))
XML::Parser.new("UTF-8").parse(utf8)
%page
XMLParser ライブラリの実装について (4-2)
%leftfill
XML::Encodingクラス
%font "typewriter", size 3, fore "yellow"
class EUCHandler
def map(i)
return i if i < 128
return -1 if i < 160 or i == 255
return -2
end
def convert(s)
Uconv.euctou2(s)
end
end
def unknownEncoding(name)
return EUCHandler.new if name =~ /^euc-jp$/i
nil
end
%page
XMLParser ライブラリの実装について (4-3)
%leftfill
Perlエンコーディングマップの流用
%font "typewriter", size 3, fore "yellow"
big5.enc
euc-kr.enc
iso-8859-2.enc
iso-8859-3.enc
iso-8859-4.enc
iso-8859-5.enc
iso-8859-7.enc
iso-8859-8.enc
iso-8859-9.enc
windows-1250.enc
x-euc-jp-jisx0221.enc
x-euc-jp-unicode.enc
x-sjis-cp932.enc
x-sjis-jdk117.enc
x-sjis-jisx0221.enc
x-sjis-unicode.enc
Shift_JIS と EUC-JP が使えないのは不便なので…
shift_jis.enc (x-sjis-cp932.enc と同じ)
euc-jp.enc (x-euc-jp-unicode.enc と同じ)
%page
DOM ライブラリの実装について
%leftfill
XML::DOM クラス
DOM level1 Core API にほぼ準拠
一部未実装 (Document#implementationなど)
文字列型非互換 (wstringではなく,多バイト文字列)
Ruby 向けのメソッド,イテレータなどを含む
XPointer (WD) 対応 ―― 福嶋正機氏による
Visotor モジュール
XML::DOM クラスで Visotor パターンを使うためのモジュール
Perl 用 XML::Grove::Visitor とほぼ同等
%page
XMLParser ライブラリの使い方 (1)
%leftfill
イベントハンドラの使用例
要素開始タグの数を数えて,各要素の使用回数を調べる。
%font "typewriter", size 3, fore "yellow"
require 'xmlparser'
class NewParser < XML::Parser
attr :elemNames
def initialize
@elemNames = {}
end
def startElement(name, attrs)
if @elemNames[name].nil?
@elemNames[name] = 1
else
@elemNames[name] += 1
end
end
end
%page
XMLParser ライブラリの使い方 (2)
%leftfill
イベントハンドラの使用例(続き)
%font "typewriter", size 3, fore "yellow"
parser = NewParser.new
begin
parser.parse($<.read)
rescue XML::ParserError
print "Error: #{$!} in line #{parser.line}\n"
end
## print result
p parser.elemNames
%page
XMLParser ライブラリの使い方 (3)
%leftfill
イテレータの使用例
%font "typewriter", size 3, fore "yellow"
require 'xmlparser'
parser = XML::Parser.new
elemNames = {}
begin
parser.parse($<.read) do |type, name, data|
case (type)
when XMLParser::START_ELEM
if elemNames[name].nil?
elemNames[name] = 1
else
elemNames[name] += 1
end
end
end
## print result
p elemNames
rescue XML::ParserError
print "Error: #{$!} in line #{parser.line}\n"
end
%page
DOM ライブラリの使い方 (1)
%leftfill
DOM と Visitor の使用例
%font "typewriter", size 3, fore "yellow"
require 'xmltreebuilder'
require 'xmltreevisitor'
class Counter < XML::DOM::Visitor
attr :elemNames
def initialize
@elemNames = {}
end
def visit_Element(elem)
name = elem.nodeName
if @elemNames[name].nil?
@elemNames[name] = 1
else
@elemNames[name] += 1
end
super
end
end
%page
DOM ライブラリの使い方 (2)
%leftfill
DOM と Visitor の使用例(続き)
%font "typewriter", size 3, fore "yellow"
parser = XML::DOM::Builder.new
begin
tree = parser.parse($<.read)
rescue XML::ParserError
print "Error: #{$!} in line #{parser.line}\n"
end
tree.documentElement.accept(c = Counter.new)
p c.elemNames
%page
もう少し実用的なプログラムの例 (1)
%leftfill
DocBook (もどき) を HTML に変換する。
%font "typewriter", size 3, fore "yellow"
テスト文書
DocBook から HTML に変換するテストです。
セクション1
ほげ
セクション2
サブセクション2-1
サブセクション2-1 です。
%page
もう少し実用的なプログラムの例 (2)
%leftfill
こんな感じに…
%font "typewriter", size 3, fore "yellow"
テスト文書
テスト文書
DocBook から HTML に変換するテストです。
2 セクション2
サブセクション2-1
サブセクション2-1 です。
%page
もう少し実用的なプログラムの例 (3)
%leftfill
要素名の置き換え
%font "typewriter", size 3, fore "yellow"
→
→
→