6

Java の汎用表現 APIを使用して Avro 1.7.0 を使用していますが、現在のスキーマ進化のケースに対処するのに問題があります。ここで扱っているシナリオは、フィールドをnullとそのプリミティブ型の和集合に変更することによって、プリミティブ型フィールドをオプションにすることです。

簡単な例を使用します。基本的に、スキーマは次のとおりです。

  • 初期: タイプのフィールドが 1 つあるレコードint
  • 2 番目のバージョン: 同じレコード、同じフィールド名ですが、型は と の和集合にnullなりましたint

Avro の仕様のスキーマ解決の章によると、このような場合の解決策は次のようになります。

リーダーが共用体であるが、ライターがそうでない場合
ライターのスキーマと一致するリーダーの共用体の最初のスキーマは、それに対して再帰的に解決されます。一致するものがない場合、エラーが通知されます。

私の解釈では、最初のスキーマでシリアル化されたデータintは、リーダーのスキーマのユニオンの一部であるため、適切に解決する必要があります。

ただし、バージョン 1 でシリアル化されたレコードをバージョン 2 を使用して読み戻すテストを実行すると、

org.apache.avro.AvroTypeException: Attempt to process a int when a union was expected.

これを正確に示すテストを次に示します。

@Test
public void testReadingUnionFromValueWrittenAsPrimitive() throws Exception {
    Schema writerSchema = new Schema.Parser().parse("{\n" +
            "    \"type\":\"record\",\n" +
            "    \"name\":\"NeighborComparisons\",\n" +
            "    \"fields\": [\n" +
            "      {\"name\": \"test\",\n" +
            "      \"type\": \"int\" }]} ");

    Schema readersSchema = new Schema.Parser().parse(" {\n" +
            "    \"type\":\"record\",\n" +
            "    \"name\":\"NeighborComparisons\",\n" +
            "    \"fields\": [ {\n" +
            "      \"name\": \"test\",\n" +
            "      \"type\": [\"null\", \"int\"],\n" +
            "      \"default\": null } ]  }");

    // Writing a record using the initial schema with the 
    // test field defined as an int
    GenericData.Record record = new GenericData.Record(writerSchema);
    record.put("test", Integer.valueOf(10));        
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    JsonEncoder jsonEncoder = EncoderFactory.get().
       jsonEncoder(writerSchema, output);
    GenericDatumWriter<GenericData.Record> writer = new 
       GenericDatumWriter<GenericData.Record>(writerSchema);
    writer.write(record, jsonEncoder);
    jsonEncoder.flush();
    output.flush();

    System.out.println(output.toString());

    // We try reading it back using the second schema 
    // version where the test field is defined as a union of null and int
    JsonDecoder jsonDecoder = DecoderFactory.get().
        jsonDecoder(readersSchema, output.toString());
    GenericDatumReader<GenericData.Record> reader =
            new GenericDatumReader<GenericData.Record>(writerSchema, 
                readersSchema);
    GenericData.Record read = reader.read(null, jsonDecoder);

    // We should be able to assert that the value is 10 but it
    // fails on reading the record before getting here
    assertEquals(10, read.get("test"));
}

私の期待が正しいかどうか (これは正常に解決されるはずですか?)、またはそのようなシナリオを処理するために avro を適切に使用していない場所を知りたいです。

4

1 に答える 1

6

プリミティブ スキーマを null とプリミティブの結合に移行するという期待は正しいです。

上記のコードの問題は、デコーダーの作成方法にあります。デコーダーは、リーダーのスキーマではなく、ライターのスキーマを必要とします。

これを行うのではなく:

JsonDecoder jsonDecoder = DecoderFactory.get().
    jsonDecoder(readersSchema, output.toString());

次のようになります。

JsonDecoder jsonDecoder = DecoderFactory.get().
    jsonDecoder(writerSchema, output.toString());

クレジットは、avro のユーザー メーリング リストの回答について Doug Cutting に送られます: http://mail-archives.apache.org/mod_mbox/avro-user/201208.mbox/browser

于 2012-08-31T21:51:30.033 に答える