シリアライゼーションクラスを自由に変更するライブラリrelocSerialの紹介
※弊社製オープンソースソフトはオープンソースソフトウェアとしてまとめているので参照されたい。relocaSrialについての全投稿は/tag/relocSerialにある。
シリアライズ・デシリアライズされるクラスを自由に変更するで書いたことだが、昔作成したプログラムコードにより、多数のユーザのデータベース内にシリアライズされたJavaオブジェクトが格納されているのだが、基本的にJavaのシリアライゼーションでは、いったんシリアライズするとパッケージ名を含むクラス名は一切変更できない。万が一変更するとデシリアライズができなくなってしまうからだ。
これを何とかしようと先の記事を書いたわけだが、めでたくライブラリ化したので、興味のある方は使ってみていただきたい。ちなみに、当該データベースのいくつかで試してみたが、現在のところ問題は発生していない。
githubはhttps://github.com/ysugimura/relocSerialになる。
Mavenリポジトリはhttps://ysugimura.github.io/mavenになる。
- This is open source library. Please refer to the github repository. The following is very simple sample of how to use this library.
使い方
簡単なJUnitテストを見るのが早いだろう。
import java.io.*;
import org.junit.*;
import static org.junit.Assert.*;
public class RelocSerializerTest {
@Test
public void test1() throws Exception {
// 昔格納したシリアライゼーションストリーム、Foo0, Bar0が格納されている。
byte[]bytes;
{
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(byteOut);
objOut.writeObject(new Foo0());
bytes = byteOut.toByteArray();
}
// RelocSerializerを作成し、新たなクラスを登録する。旧クラス名称を指定する。
// また、今後は"foo", "bar"という名前でシリアライゼーションすることにする。
RelocSerializer ser = new RelocSerializer();
ser.addTarget(Foo1.class, "foo", Foo0.class.getName());
ser.addTarget(Bar1.class, "bar", Bar0.class.getName());
// デシリアライズすると、Foo1, Bar1になっている。
Foo1 foo = ser.deserialize(bytes);
assertEquals(Bar1.class, foo.bar.getClass());
}
public static class Foo0 implements Serializable {
private static final long serialVersionUID = 1L;
Bar0 bar = new Bar0();
}
public static class Bar0 implements Serializable {
private static final long serialVersionUID = 1L;
}
public static class Foo1 implements Serializable {
private static final long serialVersionUID = 1L;
Bar1 bar = new Bar1();
}
public static class Bar1 implements Serializable {
private static final long serialVersionUID = 1L;
}
}
見ての通りだ。「昔作成した」シリアライゼーションストリームにはFoo0, Bar0が格納されているが、それを別の名前Foo1, Bar1としてデシリアライズする。
もちろんこれは「全く別のクラス」にデシリアライズすることを意図しているわけではなく、Foo0, Bar0のパッケージやクラス名が変更されたことをシミュレートするものだ。
さらに、(ここでは動作を示していないが)今後はFoo1, Bar1はそれぞれ”foo”, “bar”という名前でシリアライズすることにする。パッケージ名等はなく、単純名となる(衝突しないならパッケージを指定してもよい)。その後は”foo”, “bar”という名称で今後もシリアライズ・デシリアライズされることになる。
シリアライズクラス名称変更のための確実な方策
シリアライズ・デシリアライズするためには、それ以前に当然のことながら、addTargetにてそのクラス、新たな名称、旧名称を登録しておく必要がある。
もし、大量に既存のシリアライゼーションストリームがあり、どこに何がシリアライズされているのか不明な場合には、「可能性のあるすべて」をあらかじめ登録しておかねばならないことに注意。そうしないと意図せぬ結果になりうる。
「昔シリアライズしたもの」が無い場合
以前に、そのオブジェクトをシリアライズしたことのあるクラスのクラス名称(パッケージ含む)を変更したい場合には元の名前を指定する必要があるのだが、そうでない場合、つまり、このライブラリで新たにシリアライズする場合には、旧名称を指定する必要は無い。その場合にはnullにしておく。
ser.addTarget(Foo1.class, "foo", null);
このライブラリで新たにシリアライズされるクラスFoo1は、実際にはfooという名前でシリアライズされ、fooという名前でデシリアライズされる。
ディスカッション
コメント一覧
素晴らしい記事ありがとうございます。
参考にさせていただきます!