Java の YAML パーサー JYaml を試す

JYaml は JavaYAMLを扱うためのライブラリです。現在のバージョンは1.3となっています。本家サイトは以下です。
http://jyaml.sourceforge.net/index.html
使用するには jyaml の jar をクラスパスに追加するだけです。

YAMLについて

XMLと比較して簡素で理解しやすいファイルフォーマットで、Rubyでは主流の?ファイルフォーマットです。詳細は以下を参照ください。
http://yaml.org/


日本語だと以下がまとまってます。
http://jp.rubyist.net/magazine/?0009-YAML


シーケンスのシリアライズとデシリアライズ

オブジェクトのYAMLへのシリアライズYaml.dump メソッドを使用します。

List<String> list = new ArrayList<String>();
list.add("seq1");
list.add("seq2");

Yaml.dump(list, new File("object.yml"));


object.yml が作成され、以下の内容が出力されます。

--- 
- seq1
- seq2


上記ファイルをデシリアライズするには Yaml.load メソッドを使用します。

Object obj = Yaml.load(new File("object.yml"));
System.out.println(obj);

以下の出力となり、Listへ読み込みが行われていることが確認できます。

[seq1, seq2]

マップのシリアライズとデシリアライズ

マップに関しても同様です。

Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.put("key2", "value2");

Yaml.dump(map, new File("object.yml"));

object.yml が作成され、以下の内容が出力されます。

--- 
key2: value2
key1: value1


同じようにデシリアライズすると

Object obj = Yaml.load(new File("object.yml"));
System.out.println(obj);

マップとしてデシリアライズされていることが確認できます。

{key2=value2, key1=value1}

オブジェクトのシリアライズ

以下のようなPersonオブジェクトをシリアライズしてみます。シリアライズするクラスはデフォルトコンストラクタが必要です。

package etc9;

public class Person {
    private String name;
    private int age;
    public Person() {}
    public Person(String name, int age) {
        this.name=name;
        this.age = age;
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

シリアライズは今までと同じように

Person p = new Person("Thom", 33);
Yaml.dump(p, new File("object.yml"));

以下のようなYAMLファイルが出力されます。

--- !etc9.Person
age: 33
name: Thom

シリアライズYaml.loadとすることで YAMLの !etc9.Person の該当オブジェクトがロードされます。


dumpメソッドに追加で引数を渡し、以下のようにシリアライズすると、

Person p = new Person("Thom", 33);
Yaml.dump(p, new File("object.yml"), true);

以下のように対応クラスのタグが出力されなくなります。

--- 
age: 33
name: Thom

この場合は、以下のように、マッピング対象のクラスを指定してデシリアライズします。クラスを指定しない場合は上記の例だと、単なる Map としてデシリアライズされます。

Person p = Yaml.loadType(new File("object.yml"), Person.class);

ネストしたオブジェクト

Personクラスにchildという属性を追加してみます。

package etc9;

public class Person {
    private String name;
    private int age;
    private Person child;
    ・・・
    public Person getChild() { return child; }
    public void setChild(Person child) { this.child = child; }
}

同じ流れでシリアライズ

Person p = new Person("Thom", 33);
p.setChild(new Person("Mick", 5));
Yaml.dump(p, new File("object.yml"));

出力ファイルは以下の内容となります。

--- !etc9.Person
age: 33
child: !etc9.Person
  age: 5
  name: Mick
name: Thom

dump の第2引数に true を指定した場合は

Person p = new Person("Thom", 33);
p.setChild(new Person("Mick", 5));
Yaml.dump(p, new File("object.yml"), true);

以下のような出力となります。

--- 
age: 33
child: 
  age: 5
  name: Mick
name: Thom

シリアライズは同様に以下のように行うことができます。

Person r = Yaml.loadType(new File("object.yml"), Person.class);

複数オブジェクトのシリアライズ

複数のオブジェクトのシリアライズには YamlEncoder を使うことができます。以下のようにすると、

Person p1 = new Person("Thom", 33);
Person p2 = new Person("Mick", 5);

YamlEncoder enc = new YamlEncoder(new FileOutputStream("object.yml"));
enc.writeObject(p1);
enc.writeObject(p2);
enc.close();

以下の内容のファイルが作成されます。

--- !etc9.Person
age: 33
name: Thom
--- !etc9.Person
age: 5
name: Mick

イテレータを持つコレクションの場合には、以下のように簡単に書くこともできます。

Yaml.dumpStream(collection.iterator(), file);

シリアライズは以下のように YamlDecoder を使います。

YamlDecoder dec = new YamlDecoder(inputStream);
try {
    while (true){
        Object object = dec.readObject();
        /* do something useful */
    }
} catch (EOFException e){
    System.out.println("Finished reading stream.");
} finally
    dec.close();
}

以下のように書くこともできます。

for (Object object: Yaml.loadStream(input)){
    ・・・
}

または、以下のように型を指定することもできます。

for (Person p: Yaml.loadStreamOfType(input, Person.class)){
    ・・・
}