今回は,Ruby用のライブラリとして,Syck,RbYAML,Kwalifyの3つのライブラリを紹介します。
Syck
Syckは,Cで書かれたYAML用ライブラリです。YAML用ライブラリとしては世界で最も広く使われており,YAMLの普及に大きく貢献しました。
Syckについて
| Webサイト | http://whytheluckystiff.net/syck/ |
|---|---|
| ドキュメント | http://www.ruby-lang.org/ja/man/ index.cgi?cmd=view;name=YAML |
| バージョン | 0.60 (Ruby 1.8.6) |
| 作者 | why the lucky stiff |
インストール
Ruby 1.8以上であれば標準で付属しているため,インストールの必要はありません。
使い方
Syckの使い方はリスト1のとおりです。詳細はリファレンスマニュアル(注1)をご覧ください。
リスト1 Syckの使い方(ex-rbsyck1.rb)
require 'yaml'
## YAMLドキュメントを読み込む
str = File.read('example.yaml')
data = YAML.load(str) # 引数は文字列またはI/O
data = YAML.load_file('example.yaml') # 引数はファイル名
## YAMLストリームを読み込む
File.open('example.yaml') do |f|
YAML.load_documents(f) do |data| # 引数は文字列またはI/O
# ...
end
end
## 任意のデータをYAML文字列に変換する
data = [ {'x'=>10, 'y'=>20}, {'x'=>15, 'y'=>25} ]
str = data.to_yaml
## または
## str = YAML.dump(data) # 任意のデータをYAML文字列に変換する
## y data # 任意のデータをYAML文字列に変換して出力する
タグを変更する
yaml.dump()でインスタンスオブジェクトをYAML文字列に変換した場合,たとえば「!ruby/object:Color」のようなタグがつきます。しかしこれだとRuby限定になり,ほかの言語のYAMLパーサで読み込むとエラーになるため,データ交換する場合には都合が悪いです。
これを回避するには,以下のメソッドを使ってクラスに対応するタグを指定します(リスト2)。
- Object#taguri()
- オブジェクトを表すタグを返します。
- YAML.add_builtin_type(ytag) {|type, val| ...}
- データからオブジェクトを復元するブロックを登録します。
またObject#to_yaml_style()を再定義することで,フロースタイルで出力させることができます。
リスト2 クラスに対応するタグを指定する(ex-rbsyck1.rb)
require 'yaml'
class Color
def initialize(r, g, b)
@r = r; @g = g; @b = b
end
attr_accessor :r, :g, :b
## タグを「!ruby/object:Color」から「!color」へ変更する
def taguri
return 'tag:yaml.org,2002:color' # !color
#return 'x-private:color' # !!color
end
## フロースタイルで表示する
def to_yaml_style
return :inline
end
end
## 実行例
obj = Color.new(255, 128, 0)
puts YAML.dump(obj) #=> --- !color {b: 0, g: 128, r: 255}
## 「!color」タグのときにColorオブジェクトを生成する
YAML.add_builtin_type('color') do |type, val|
YAML.object_maker(Color, val)
end
## 「!!color」タグのときにColorオブジェクトを生成する
#YAML.add_private_type('color') do |type, val|
# YAML.object_maker(Color, val)
#end
## 実行例
str = '!color {r: 255, g: 128, b: 0}'
p YAML.load(str) #=> #<Color:0x5da9e4 @b=0, @g=128, @r=255>
日本語の扱い
Rubyの文字列に日本語が含まれる場合,YAMLドキュメントに変換すると,たとえば「'いろは'」が「!binary 44GE44KN44Gv」のようにバイナリデータとして変換されてしまいます。この問題は,根本的にはRubyにおいて文字列とバイナリデータの区別がないことが原因であり,そのためにSyckは安全を期して「ASCII以外の文字を(ある一定の比率で)含むような文字列はバイナリデータ」とみなしています。
しかしこれは不便である人も多いでしょう。この回避策としては,2つあります。1つ目はSyckのソースコードにパッチを当てることです。Rubyのソースコードをダウンロードし,リスト3のパッチを適用してコンパイルします。そしてリスト4のようString#is_binary_data?()を上書きしてバイナリとみなされないようにすると,日本語文字列が二重引用符で囲まれて出力されるようになります。
リスト3 日本語をエスケープしないようにするパッチ
--- ruby-1.8.6-p111/ext/syck/emitter.c Wed Jan 09 12:57:49 2008 +0900
+++ ruby-1.8.6-p111/ext/syck/emitter.c Wed Jan 09 14:07:38 2008 +0900
@@ -778,7 +778,8 @@ syck_emitter_escape( SyckEmitter *e, cha
int i;
for( i = 0; i < len; i++ )
{
- if( (src[i] < 0x20) || (0x7E < src[i]) )
+ /*if( (src[i] < 0x20) || (0x7E < src[i]) )*/
+ if( 0 )
{
syck_emitter_write( e, "\\", 1 );
if( '\0' == src[i] )
.--------------------
リスト4 YAML.dump()で日本語を出力する
require 'yaml'
class String
def is_binary_data?
false ## 日本語がバイナリとみなされるのを防ぐ
end
end
puts YAML.dump(['alphabet', '日本語'])
### 実行結果:
## ---
## - alphabet
## - "日本語"
もう1つの方法は,Ya2YAMLを使うことです。Ya2YAMLをインストールして$KCODEを'utf8'に設定すると,文字列をUTF8として扱ってくれます(リスト5)。Ya2YAMLはRubyGemsを使って「gem install ya2yaml」とすればインストールできます。
ただし,循環参照したデータには対応していません。
リスト5 ya2yamlを使うと,日本語がUTF8として出力される(ex-ya2yaml1.rb)
## 日本語文字列は、バイナリデータとして扱われてしまう
str = 'いろは'
require 'yaml'
p str.to_yaml #=> "--- !binary |\n44GE44KN44Gv\n\n"
## ya2yamlを使うと、UTF8の文字列はそのまま出力される
require 'rubygems' # RubyGemsでインストールした場合
require 'ya2yaml'
$KCODE = 'utf8'
p str.ya2yaml #=> "--- いろは\n"
## Object#to_yamlをya2yamlで置き換えてもよい
[Object, String, Array, Hash].each do |klass|
klass.class_eval { alias to_yaml ya2yaml }
end
p str.to_yaml #=> "--- いろは\n"
不具合
筆者が試したところ,以下のような不具合がありました(Ruby 1.8.6で確認)。
日付(タイムスタンプ)の解析に不具合があり,「2008-01-01T12:34:56Z」という形式しか解釈されない(リスト6)。
対応するアンカーのないエイリアスがあっても,エラーにならない。
フロースタイルのデータにエイリアスをつけるとエラーになる場合がある(リスト7)。
リスト6 仕様上は正しいがSyckでは文字列と解釈される形式
- 2008-01-01T12:34:56Z
- 2008-01-01 12:34:56
- 2008-01-01 12:34:56Z
- 2008-01-01T12:34:56
- 2008-01-01 12:34:56 +9
- 2008-01-01 12:34:56.0
リスト7 Syckで読み込むとエラーになる例
- &m1
{a: 10}
- &s1
Foo
その他
Syckは,Ruby向けに次のような独自機能を実装しています。
- 「:foo」のようなデータは,文字列ではなくRubyのSymbolオブジェクトに変換されます。

