スキーマ バージョン 1 を使用してオブジェクトをシリアル化し、後でスキーマをバージョン 2 に更新した場合 (フィールドを追加するなど)、後でオブジェクトを逆シリアル化するときにスキーマ バージョン 1 を使用する必要がありますか? 理想的には、スキーマ バージョン 2 のみを使用し、オブジェクトが最初にシリアル化された後にスキーマに追加されたフィールドのデフォルト値を逆シリアル化されたオブジェクトに持たせたいと考えています。
たぶん、いくつかのコードがよりよく説明されます...
スキーマ 1:
{"type": "record",
"name": "User",
"fields": [
{"name": "firstName", "type": "string"}
]}
スキーマ 2:
{"type": "record",
"name": "User",
"fields": [
{"name": "firstName", "type": "string"},
{"name": "lastName", "type": "string", "default": ""}
]}
一般的な非コード生成アプローチを使用:
// serialize
ByteArrayOutputStream out = new ByteArrayOutputStream();
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
GenericDatumWriter writer = new GenericDatumWriter(schema1);
GenericRecord datum = new GenericData.Record(schema1);
datum.put("firstName", "Jack");
writer.write(datum, encoder);
encoder.flush();
out.close();
byte[] bytes = out.toByteArray();
// deserialize
// I would like to not have any reference to schema1 below here
DatumReader<GenericRecord> reader = new GenericDatumReader<GenericRecord>(schema2);
Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);
GenericRecord result = reader.read(null, decoder);
結果は EOFException になります。jsonEncoder
結果を AvroTypeException で使用する。
schema1 と schema2 の両方をコンストラクターに渡すとうまくいくことはわかっていGenericDatumReader
ますが、以前のすべてのスキーマのリポジトリを保持する必要はなく、特定のオブジェクトをシリアル化するためにどのスキーマが使用されたかを追跡する必要もありません。
また、コード生成アプローチも試しました。最初に、schema1 から生成された User クラスを使用してファイルにシリアル化します。
User user = new User();
user.setFirstName("Jack");
DatumWriter<User> writer = new SpecificDatumWriter<User>(User.class);
FileOutputStream out = new FileOutputStream("user.avro");
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
writer.write(user, encoder);
encoder.flush();
out.close();
次に、スキーマをバージョン 2 に更新し、User クラスを再生成して、ファイルの読み取りを試みます。
DatumReader<User> reader = new SpecificDatumReader<User>(User.class);
FileInputStream in = new FileInputStream("user.avro");
Decoder decoder = DecoderFactory.get().binaryDecoder(in, null);
User user = reader.read(null, decoder);
しかし、EOFException も発生します。
比較のために、私がやろうとしていることはprotobufsで動作するようです...
フォーマット:
option java_outer_classname = "UserProto";
message User {
optional string first_name = 1;
}
シリアライズ:
UserProto.User.Builder user = UserProto.User.newBuilder();
user.setFirstName("Jack");
FileOutputStream out = new FileOutputStream("user.data");
user.build().writeTo(out);
オプションの last_name を format に追加し、UserProto を再生成し、逆シリアル化します。
FileInputStream in = new FileInputStream("user.data");
UserProto.User user = UserProto.User.parseFrom(in);
予想どおり、user.getLastName()
空の文字列です。
このようなことは Avro で実行できますか?