我正在使用其Java's generic representation API使用Avro 1.7.0,我在处理当前的架构演变案例时遇到了问题。我们在这里讨论的场景是通过将字段更改为null
和原始类型的并集来使原始类型字段可选。
我将使用一个简单的例子。基本上,我们的模式是:
int
null
和int
的联合根据Avro规范的schema resolution chapter,此类案件的解决方案应为:
如果读者是工会,但作家不是
读者联合中与编写者模式匹配的第一个模式以递归方式解决。如果不匹配,则发出错误信号。
我的解释是我们应该正确解析使用初始模式序列化的数据,因为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处理这种情况。
答案 0 :(得分:4)
将原始模式迁移到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