我编写了一个类AvroIterator<T>
,它是一些avro调用的小型包装器,允许用户指定文件名、模式文件名和从avroGenericRecord
获取T
的方法。
这适用于基元类型。例如,在我的测试中,我有这样一个类:
public class TripleData {
private final int x;
private final double y;
private final String z;
public TripleData(int x, double y, String z) {
this.x = x;
this.y = y;
this.z = z;
}
}
static TripleData getTripleData(GenericRecord record) {
return new TripleData(
(int)record.get("x"),
(double)record.get("y"),
((CharSequence)record.get("z")).toString()
);
}
我得到一个迭代器如下:
Iterator<TripleData> iterator = new AvroIterator<>(TRIPLE_DATA_FILENAME_1,
TRIPLE_DATA_SCHEMA_FILENAME,
AvroIteratorTest::getTripleData);
当我想要反序列化包含逻辑类型的类时,就会出现问题。目前我正在尝试约会。
private static class DatedData {
private final LocalDate date;
private final double x;
private final int y;
private DatedData(LocalDate date, double x, int y) {
this.date = date;
this.x = x;
this.y = y;
}
}
我的模式是
{
"type": "record",
"name": "DatedData",
"fields": [
{"name": "date", "type": "int"},
{"name": "x", "type": "double"},
{"name": "y", "type": "int"}
]
}
当我尝试编写等效的getDatedData(GenericRecord record)
方法时,我不能遵循与TripleData
相同的模式。如果我调用(LocalDate)record.get("date")
,就会抛出一个ClassCastException
,因为那个record.get
调用返回一个int
。
我不想只使用对LocalDate.ofEpochDay
的调用,因为从那时起,我就依赖于avro文档来了解如何从基元类型转换为逻辑类型,并且没有检查模式是否真的将此字段定义为日期。
创建avro的TimeConversions.DateConversion
类的实例,并调用其fromInt
方法进行转换,这看起来是"正确的"做法。这有的签名
public LocalDate fromInt(Integer daysFromEpoch, Schema schema, LogicalType type)
因此,我在DatedData
中添加了一个内部类Converter
,它将保存Schema
,并具有一种转换方法:
public static class Converter {
private final Schema schema;
private final LogicalType dateLogicalType;
private final TimeConversions.DateConversion innerConverter;
Converter(Schema schema) {
this.schema = schema;
this.dateLogicalType = schema.getField("date").schema().getLogicalType();
innerConverter = new TimeConversions.DateConversion();
}
Converter(String schemaFilename) throws IOException {
this(new Schema.Parser().parse(new File(schemaFilename)));
}
public DatedData getFromGenericRecord(GenericRecord record) {
LocalDate date = innerConverter.fromInt((int)record.get("date"), schema, dateLogicalType);
return new DatedData(
date,
(double)record.get("x"),
(int)record.get("y")
);
}
}
然后我创建我的迭代器如下:
Iterator<DatedData> iterator = new AvroIterator<>(DATED_DATA_FILENAME,
DATED_DATA_SCHEMA_FILENAME,
new DatedData.Converter(DATED_DATA_SCHEMA_FILENAME)::getFromGenericRecord);
这是有效的,但如果我用null
替换fromInt
调用中的Schema
和LogicalType
,它仍然有效。如果我修改我的模式以使";日期";只是具有类型CCD_ 20。也就是说,我无法用这种方法验证模式。
如果我试图将没有真正序列化为日期的内容转换为某种失败,我希望从中获得一些保护,以防avro更改日期序列化的定义。此外,我希望任何阅读代码的人都能说";好吧,很明显,这使用了Avro的方法来反序列化日期,我不必去阅读Avro文档">
我能做些更好的事情吗?
只需将逻辑类型添加到架构中即可:
{
"type": "record",
"name": "DatedData",
"fields": [
{"name": "date", "type": { "type": "int", "logicalType": "date"} },
{"name": "x", "type": "double"},
{"name": "y", "type": "int"}
]
}