MessagePack for Java 作りかけリリース!

バイナリシリアライズ形式 MessagePackJava版の、作りかけをリリースしました^^;
リアライザやデシリアライザの本体は実装できていますが、例外やインタフェースの完成度はまだ高くないです。開発者募集中!

実装はPure Javaです。JNIは使っていません。

MessagePack は Ruby, Perl, Python, PHP などのLLにも対応しているので、JavaとLLの間で簡単にオブジェクトを交換できるようになります。

ベンチマークテスト

他のシリアライズ形式と速度を比較してみたところ、↓このような結果になりました。



MessagePackは、MessagePackの型からJavaの型に変換(型チェック)する方法の違いによって、4種類の方法でテストしました:

msgpack-specific
スキーマから型変換クラスを生成してコンパイルし、デシリアライズしながら型変換したもの
msgpack-indirect
スキーマから型変換クラスを生成してコンパイルし、いったん汎用的な型にデシリアライズしたあと、型変換したもの(MessagePack for C++と同じ動作)
msgpack-generic
スキーマから型変換クラスを生成せずに、実行時に翻訳しながら型変換したもの
msgpack-dynamic
型変換していないもの(MessagePack for Ruby, Perl, Pythonと同じ動作)


MessagePack for Java は相当に高速で、シリアライズ後のサイズも非常に小さくなります。
Java の Serializable と比べて10倍以上高速で、Protocol Buffers や Thrift よりも高速です。

使い方

シリアライズ

オブジェクトをシリアライズするには、org.msgpack.Packer クラスを使います。シリアライズ可能な任意のオブジェクト(Map, List, String, Int, Long, Float, ...etc)を pack メソッド に渡すと、コンストラクタに渡した OutputStream にシリアライズしてくれます。

import org.msgpack.*;
import java.io.*;
import java.util.*;

public class SerializeExample {
    public static void main(String[] args) throws IOException {
        // 出力先のOutputStream
        OutputStream out = new ByteArrayOutputStream();

        // シリアライザを初期化
        Packer pk = new Packer(out);

        // 何か適当なオブジェクトを作って...
        ArrayList<String> array = new ArrayList();
        array.add("MessagePack");
        array.add("test");

        // シリアライズ
        pk.pack(array);
    }
}
シリアライズ

シリアライズには org.msgpack.Unpacker クラスを使います。コンストラクタに InputStream を渡すと、拡張for文(かイテレータ)を使って次々にオブジェクトを取り出せます。

import org.msgpack.*;
import java.io.*;
import java.util.*;

public class DeserializeExample {
    public static void main(String[] args) throws IOException {
        // 入力元のInputStream
        InputStream in = System.in;

        // デシリアライザを初期化(ストリームシリアライザ)
        Unpacker pac = new Unpacker(in);

        // 拡張for文で取り出す
        for(Object obj : pac) {
            System.out.println(obj);
        }
    }
}
型チェック付きのデシリアライズ

シリアライズしたオブジェクトの型をチェックして、予期しない型だったら例外を送出するようにすることができます。
型チェックをするには、まず「スキーマ」を書きます:

    // Map<String, Integer> を表すスキーマ
    Schema mapSchema = Schema.parse("(map string int)");

    // List< List<Long> > を表すスキーマ
    static Schema arraySchema = Schema.parse("(array (array long))");

このスキーマをorg.msgpack.Unpackerにセットすると、デシリアライズされたオブジェクトが正しい型かどうかをチェックしてくれます。

import org.msgpack.*;
import java.io.*;
import java.util.*;

public class CheckedDeserializeExample {
    // Map<String, Integer> を表すスキーマ
    static Schema testSchema = Schema.parse("(map string int)");

    public static void main(String[] args) throws IOException {
        // シリアライザを初期化
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Packer pk = new Packer(out);

        // シリアライズするオブジェクト
        Map<String, Integer> map = new HashMap();
        map.put("type", 0);
        map.put("check", 1);
        map.put("test", 2);

        // シリアライズ
        pk.pack(map);  // これは予期した型
        pk.pack("こっちは予期しない型");

        // デシリアライザを初期化
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        Unpacker pac = new Unpacker(in).useSchema(testSchema); // useSchemaでスキーマを指定

        // 予期しない型が届いたらエラーになる
        for(Object obj : pac) {
            Map<String, Integer> msg = (Map<String, Integer>)obj;
            System.out.println(msg);
        }
    }
}
型チェック付きのシリアライズ

シリアライズ時にも型チェックができます。pack メソッドの代わりに packWithSchema メソッド を使うと、正しくない型のオブジェクトをシリアライズしようとしたときにエラーになります。

import org.msgpack.*;
import java.io.*;
import java.util.*;

public class CheckedSerializeExample {
    // Map<String, Integer> を表すスキーマ
    static Schema testSchema = Schema.parse("(map string int)");

    public static void main(String[] args) throws IOException {
        // シリアライザを初期化
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Packer pk = new Packer(out);

        // シリアライズするオブジェクト
        Map<String, Integer> map = new HashMap();
        map.put("type", 0);
        map.put("check", 1);
        map.put("test", 2);

        // シリアライズ
        pk.packWithSchema(map, testSchema);  // これは成功する
        pk.packWithSchema("こっちはエラーになる", testSchema);
    }
}
型変換

シリアライズしながら型チェックするだけでなく、いったん汎用的な型にデシリアライズしてから、後で型変換することができます。
途中までは RubyPython のように動的型として扱って、途中から静的な型に変換して使いたいときに便利です。
型変換をするには、Schema.convert メソッドを使います。

public class ConverExample {
    // Map<String, Integer> を表すスキーマ
    static Schema testSchema = Schema.parse("(map string int)");

    public static void main(String[] args) throws IOException {
        // シリアライザを初期化
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Packer pk = new Packer(out);

        // シリアライズ
        Map<String, Integer> map = new HashMap();
        map.put("type", 0);
        map.put("check", 1);
        map.put("test", 2);
        pk.pack(map);

        // デシリアライザを初期化
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        Unpacker pac = new Unpacker(in); // useSchemaしない

        for(Object obj : pac) {
            // ここで Schema.convert する
            // 予期しない型だったらエラーになる
            Map<String, Integer> msg = (Map<String, Integer>)testSchema.convert(obj);
            System.out.println(msg);
        }
    }
}
クラスのスキーマ

スキーマを使ってクラスを定義することもできます。

(class MediaContent
    (package serializers.msgpack)
    (field image (array (class Image
            (field uri string)
            (field title string)
            (field width int)
            (field height int)
            (field size int))))
    (field media (class Media
            (field uri string)
            (field title string)
            (field width int)
            (field height int)
            (field format string)
            (field duration long)
            (field size long)
            (field bitrate int)
            (field person (array string))
            (field player int)
            (field copyright string)))
    )

このスキーマからJavaソースコードを生成することができます。

開発者募集中!

クラスの設計や例外の設計も含めて、作りかけです。(作りかけと言うより、使わないので作れない…)
MessagePack for Java をどこかで使いたい機会があったら、ぜひ続きを作ってみてください。