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

第6回Java編

今回は、Java用のライブラリとして、JvYAML、JYamlの2つのライブラリを紹介します。

JvYAML

JvYAMLはRbYAMLをJavaへ移植したものであり[1]⁠、JRubyで使われています。

JvYAMLについて
Webサイトhttps://jvyaml.dev.java.net/
servlets/ProjectDocumentList
バージョン0.2.1
ドキュメントなし
作者Ola Bini

インストール

https://jvyaml.dev.java.net/からjvyaml-bin-0.2.1.tar.gzをダウンロードして解凍し、lib/jvyaml.jarを適切なディレクトリにコピーしてくださいリスト1⁠。

リスト1 JvYAMLのインストール
$ wget --no-check-certificate https://jvyaml.dev.java.net/files/documents/5215/41454/jvyaml-bin-0.2.1.tar.gz
$ tar xzf jvyaml-bin-0.2.1.tar.gz
$ cd jvyaml-0.2.1/
$ cp lib/jvyaml.jar $HOME

使い方

JvYAMLの基本的な使い方はリスト2のとおりです。YAMLドキュメントが(たとえコメントであっても)日本語を含む場合は、YAML.load(Reader)ではなくてYAML.load(String)を使うようにしてください。

リスト2 JvYAMLの使い方(JvYAMLExample1.java)
import java.util.*;
import java.io.*;
import org.jvyaml.YAML;

public class JvYAMLExample1 {
    public static void main(String[] args) throws IOException {
        /// YAMLドキュメントを読み込む
        /// (日本語を含まないのであれば
        ///     Reader reader = new FileReader("example.yaml");
        ///     Object ydoc = YAML.load(reader);
        ///  でもよい)
        InputStream inputStream =
            args.length > 0 ? new FileInputStream(args[0]) : System.in;
        Reader reader = new InputStreamReader(inputStream, "UTF-8");
        reader = new BufferedReader(reader);
        StringBuffer buf = new StringBuffer();
        int ch;
        while ((ch = reader.read()) >= 0)
            buf.append((char)ch);
        reader.close();
        Object ydoc = YAML.load(buf.toString());
        System.out.println(ydoc);

        /// YAMLストリームを読み込む
        reader = new FileReader("example.yaml");
        List ydocs = YAML.loadAll(reader);
        reader.close();
        System.out.println(ydocs);

        /// 任意のデータをYAML文字列に変換する
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", 101);
        map.put("name", "Foo");
        List<Map> list = new ArrayList<Map>();
        list.add(map);
        System.out.println(YAML.dump(list));
    }
}

タグを変更する

JvYAMLでタグを変更する方法は不明です。

日本語の扱い

読み込み(YAML.load())については、YAML.load(java.lang.String)は問題ありませんが、YAML.load(java.io.Reader)はエラーになります[2]⁠。

書き出し(YAML.dump())については、日本語を含む文字列があるとエラーになります。解決策は今のところありません。

不具合

筆者が試したところ、以下のようなバグや制限が見つかりました。

YAML.load()
  • 循環参照しているとエラーになる(再帰的なエイリアスが解釈できない)
  • 文字列をブロックスタイルで記述したとき、最終行の改行が取り除かれる
  • マッピングのデフォルト値を認識しない
YAML.dump()
  • 循環参照しているデータがあるとエラーになる
  • publicでないクラスのオブジェクトが復元できない

その他

もとはJRubyのために開発されたJvYAMLですが、現在のJRubyではStringのかわりにbyte配列を使うようにした「JvYAMLb」という別のライブラリが使われています。JvYAMLと違い、JvYAMLbは循環したデータも問題なく扱えますが、byte配列を使っているため日本語がまったく使えません。

作者のOla Biniは、今のところJvYAMLbのみメンテナンスしており、JvYAMLは放置されています。そのため、JvYAMLの不具合が修正される可能性は低いでしょう。

JYaml

JYamlは、Javaで書かれたYAML用ライブラリです。JvYAMLよりは致命的な不具合が少なく、また日本語も問題なく扱えるため、JvYAMLよりお勧めです。

JYamlについて

インストール

インストールは、http://downloads.sourceforge.net/jyaml/jyaml-1.3.jarを適当なディレクトリにダウンロードするだけです。

使い方

基本的な使い方はリスト3のとおりです。

リスト3 JYamlの基本的な使い方(JYamlExample1.java)
import java.util.*;
import java.io.*;
import org.ho.yaml.Yaml;

public class JYamlExample1 {
    public static void main(String[] args) throws IOException {
        /// YAMLドキュメントを読み込む
        InputStream inputStream =
            args.length > 0 ? new FileInputStream(args[0]) : System.in;
        Reader reader = new InputStreamReader(inputStream, "UTF-8");
        reader = new BufferedReader(reader);
        Object ydoc = Yaml.load(reader);  // Yaml.load(String)も可
        System.out.println(ydoc);

        /// YAMLストリームを読み込む
        /// (最初が「---」で始まってる必要がある)
        reader = new FileReader("example.yaml");
        for (Object ydoc2: Yaml.loadStream(reader)) {
            System.out.println(ydoc2);
        }
        reader.close();

        /// 任意のデータをYAML文字列に変換する
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", 101);
        map.put("name", "どれみ");
        List<Map> list = new ArrayList<Map>();
        list.add(map);
        map.put("self", map);
        System.out.println(Yaml.dump(list));
    }
}

タグを変更する

Yaml.dump()でインスタンスオブジェクトをYAML文字列に変換した場合、たとえば「!com.example.Color」のようなタグがつきます。しかしこれだとJava限定になり、ほかの言語のYAMLパーサで読み込むとエラーになるため、データ交換する場合には都合が悪いです。

これを回避するには、設定ファイル「jyaml.yml」を用意し、そこにタグ名とクラス名との対応を指定しますリスト4⁠。このjyaml.ymlは、カレントディレクトリか、またはクラスパスで指定されたディレクトリに置いてください。

リスト4 jyaml.yml: 設定ファイル
transfers:
  color: com.example.Color

リスト5リスト6はサンプルプログラムです。ここでは次の2つを行っています。

  • 「!color」タグがついたマッピングからcom.exaple.Colorオブジェクトを復元する
  • com.example.Colorオブジェクトを「!color」タグがついたYAML文字列に変換する
リスト5 タグを変更する(JYamlExample2.java)
import java.util.*;
import java.io.*;
import org.ho.yaml.Yaml;

public class JYamlExample2 {
    public static void main(String[] args) throws IOException {
        /// 「!color」タグがついたデータを読み込む
        String input = "- !color {red: 255, green: 128, blue: 0}\n";
        Object ydoc = Yaml.load(input);
        System.out.println(ydoc);
        /// 実行結果(com.example.Colorオブジェクトが生成される):
        ///   [com.example.Color@ce5b1c]

        /// com.example.ColorオブジェクトをYAML文字列に変換する
        com.example.Color color = new com.example.Color(255, 128, 1);
        System.out.print(Yaml.dump(color));
        /// 実行結果(「!color」タグが使われる):
        ///   --- !color
        ///   blue: 1
        ///   green: 128
        ///   red: 255
    }
}
リスト6 com.example.Colorクラス(com/example/Color.java)
package com.example;

public class Color {
    private int red, green, blue;

    public Color() { };
    public Color(int r, int g, int b) {
        setRed(r);
        setGreen(g);
        setBlue(b);
    }

    public int getRed() { return red; }
    public void setRed(int r) { red = r; }
    public int getGreen() { return green; }
    public void setGreen(int g) { green = g; }
    public int getBlue() { return blue; }
    public void setBlue(int b) { blue = b; }
}

なお設定ファイルjyaml.ymlには、ほかにも文字コードやインデント幅などを指定できます。詳しくはJYamlのドキュメントを参照してください。

日本語の扱い

特に問題はないようです。YAMLドキュメントが日本語を含む場合、JvYAMLではYAML.load(Reader)やYAML.dump()がエラーになったのに対し、JYamlではYaml.load(Reader)もYaml.dump()も問題なく動作します。

不具合

筆者が試した限りでは次のような問題点がありました。

Yaml.load()
  • フロースタイルが正しくパースされない場合があるリスト7
  • 正しいYAMLドキュメントでもエラーになる場合があるリスト8
  • 未定義の名前を使ったエイリアスがあってもエラーにならない
Yaml.loadStream()
  • 入力が「---」で始まっていないと最初のドキュメントしかパースされない
Yaml.dump()
  • たとえば「!java.util.HashMap」のように、本来省略されるはずのタグが明示されることがあるリスト9
リスト7 JYamlのYaml.load()で正しくパースできない例
# { null: {x: 10, y: 20}} と解釈されてしまう
{x: 10, y: 20} 
リスト8 JYamlのYaml.load()でエラーになる例
# 'End of document expected'という例外が発生する
- &m1
  foo
- bar
リスト9 Yaml.dump()の結果(マッピングのタグが明示される)
- !java.util.HashMap
  y: 20
  x: 10

その他

JvYAMLとJYamlを比べると、どちらも不具合が多いのですが、多少はJYamlのほうがましなようです。実際に使う場合は、動作をよく確かめてから使うようにしてください。

おすすめ記事

記事・ニュース一覧