言語別 YAML用ライブラリ徹底解説

第2回 Ruby編

この記事を読むのに必要な時間:およそ 7 分

今回は,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"
注2
http://rubyforge.org/projects/ya2yaml/

不具合

筆者が試したところ,以下のような不具合がありました(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オブジェクトに変換されます。

著者プロフィール

桑田誠(くわたまこと)

プログラマー。Javaに対するLL,XMLに対するYAMLなど,複雑なことをシンプルにするような技術に興味をもつ。

URLhttp://www.kuwata-lab.com/

コメント

コメントの記入