从Java中的avro GenericRecord中获取LocalDate



我编写了一个类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调用中的SchemaLogicalType,它仍然有效。如果我修改我的模式以使";日期";只是具有类型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"}
]
}

相关内容

  • 没有找到相关文章

最新更新