クラスローダをまたがったモデルのコンバート

2つのモデルクラスがあったとします*1

model-1.jar

package com.example.a;
public class Model {
    public int id;
    public int foo;
    public String string;
    public Model child;
}

model-2.jar

package com.example.b;
public class Model {
    public int id;
    public int bar;
    public List<String> strings;
    public Model child;
}

model-1は旧バージョン、model-2は新バージョンだと思ってください。で、model-1からシリアライズされたデータファイル「old.obj」をmodel-2版として「new.obj」にコンバートしたいと考えています。

ちなみに、Serializerクラスはxstreamのプロキシです。

上記の通り、2つのモデルクラスのパッケージが異なれば、以下のようにコンバートします。

converter 1

model-1.jarとmodel-2.jarにクラスパスを通して…。

package com.example;
public class Converter {
    public static void main(String[] args) {
        // 旧モデルのロード
        com.example.a.Model oldModel =
                (com.example.a.Model) Serializer.deserialize(
                    "old.obj", getClass().getClassLoader());

        // 新モデルの生成
        com.example.b.Model newModel = new com.example.b.Model();

        // データ移行
	newModel.id = oldModel.id;
	newModel.bar = oldModel.foo;
        newModel.strings = new ArrayList();
        newModel.strings.add(oldModel.string);
        // childも、そのまたchildも、上手くやってデータ移行する

        // 新モデルファイルにシリアライズ
        Serializer.serialize(newModel, "new.obj", getClass().getClassLoader());
    }
}

しかし、2つのモデルクラスのパッケージが同一(com.example)だった場合、FQCNが衝突(どちらもcom.example.Model)してしまう為、同じクラスローダでロードすることができません。従って、どちらか片方(ここでは旧モデルmodel-1)のjarは別のクラスローダで読み込む必要があります。

converter 2

model-2.jarだけにクラスパスを通して…。

package com.example;
import java.net.URLClassLoader;
public class Converter {
    public static void main(String[] args) {
        // 旧モデルのロード
        ClassLoader cl = new URLClassLoader("file://path/to/model-1.jar");
        Object oldModel = Serializer.deserialize("old.obj", cl);

        // 新モデルの生成
        Model newModel = new Model();

        // データ移行
        /* newModel <= oldModel */

        // 新モデルファイルにシリアライズ
        Serializer.serialize(newModel, "new.obj", getClass().getClassLoader());
    }
}

この様にして、旧モデルのインスタンスをoldModelに取得することができました。

が、Object型として受け取ることはできるのですが、ここでModelにキャストする訳にはいきません。Converterクラス内ではModelは新モデルですから。

この状況で、oldModelからnewModelにデータを移し替える作業をどのように行えば良いのか、悩んでいます。リフレクションでゴリゴリ移行するしか手がないのでしょうか。

この例くらいに簡単なモデルであればそれも良いかもしれませんが、Jiemamyのモデルは複雑過ぎて、ミスらない自信がありませんww

せめて、変更のないプロパティ(ここではidとchild)くらいは自動で移行したいものです。理想型としては、「変更内容」をContext情報としてconvertメソッドに渡してうまくコンバートしたいっすね…。

// 変更内容を記述
ConvertContext ctx = new ConvertContext();
ctx.rename("foo", "bar");
ctx.toList("string", "strings");

// 新モデルにコンバート
Model newModel = new Converter().convert(oldModel, ctx);

理想型はこんな雰囲気かなぁ。なんかややこしそう>< (childによる参照がループする可能性も…)

まぁ、Jiemamyのデータコンバート機能を作りたい訳なんですけどね。(そろそろデータ移行に対応しないと誰も使ってくれない件w)

上手い方法、ご存知ないでしょうか。皆さんのお知恵を拝借…。

*1:下記コードはコンパイラを通さずに素で書いたので、色々ミスってるかも。あ、例外処理とかしてないやw