6

我正在使用 Avro 1.7.0 使用其Java 的通用表示 API,并且在处理我们当前的模式演变案例时遇到了问题。我们在这里处理的场景是通过将字段更改为与该原始类型的联合来使原始类型字段成为可选的null

我将使用一个简单的例子。基本上,我们的模式是:

  • 初始:具有一个类型字段的记录int
  • 第二个版本:相同的记录,相同的字段名称,但类型现在是和的null并集int

根据 Avro 规范的架构解析章节,这种情况的解析应该是:

如果读者是一个联合,但作者不是
读者联合中与作者模式匹配的第一个模式将针对它递归解析。如果没有匹配,则发出错误信号。

我的解释是,我们应该正确地解析使用初始模式序列化的数据,因为int它是读者模式中联合的一部分。

但是,当运行使用版本 2 读回使用版本 1 序列化的记录的测试时,我得到

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());

归功于 Doug Cutting 在 avro 的用户邮件列表中的答案: http ://mail-archives.apache.org/mod_mbox/avro-user/201208.mbox/browser

于 2012-08-31T21:51:30.033 回答