我是Avro的新手,我正在尝试编写一些代码来序列化一些嵌套对象。
对象的结构如下所示:
class Parcel {
String recipe;
Map<Integer, PluginDump> dumps;
}
class PluginDump {
byte[] state;
Map<String, Param> params;
}
class Param {
Type type; //can be e.g. StringType, BooleanType, etc
Object value;
}
所以我不能使用静态avro架构 - 每个PluginDump都会有不同的架构,具体取决于其中的类型。
我编写了一些可以基于单个PluginDump生成Schema的代码。
因此,在序列化包裹时,我如何放置&#39;每个PluginDump条目?
这是我的代码:
Schema parcelSchema = AvroHelper.getSchema(p);
GenericRecord parcelRecord = new GenericData.Record(parcelSchema);
parcelRecord.put("recipe", p.getRecipe().toJson());
for (Map.Entry<Integer, PluginDump> entry : p.getDumps().entrySet()) {
PluginDump dump = entry.getValue();
Integer uid = entry.getKey();
Schema dumpSchema = AvroHelper.getSchema(dump);//will be different for each PluginDump
parcelRecord.put(????
有什么想法吗?
我觉得我的方法是错误的,但我在动态模式生成或嵌套地图的文档中找不到任何示例。
答案 0 :(得分:0)
1 当您获得GenericRecord parcelRecord = new GenericData.Record(parcelSchema);
时,您的记录中有两个字段:配方和转储,因此您无法遍历转储,您必须将准备好的地图放入转储在第二个记录领域,就像你为食谱做的那样:parcelRecord.put("dumps", dumps);
。但在这种情况下,您将获得ClassCastException,因为PluginDump无法转换为org.apache.avro.generic.IndexedRecord,因此您需要在parcelRecord中放入GenericRecords的Map。你也需要Map<String, Param> params
这个,因为Param也不能转换为IndexedRecord。
2 然后,我认为使用Lists而不是Maps更好,导致avro不能很好地使用不同类型的键和值的地图。
3 关于Param类:如果您将使用自动生成的模式,Param类将如下所示。
"type": "record",
"name": "Param",
"fields": [
{
"name": "type",
"type": {
"type": "record",
"name": "Type",
"namespace": "java.lang.reflect",
"fields": []
}
},
{
"name": "value",
"type": {
"type": "record",
"name": "Object",
"namespace": "java.lang",
"fields": []
}
}
]
就avro使用java.lang.reflect而言,反序列化后会丢失类型字段,avro将不知道它是什么类型。
如果你想为每个Param手动生成avro-schema,考虑到它的类型,你可以这样做(我使用了来自apache commons-lang3的ClassUtils.getClass,导致标准的Class.forName方法没有&#39 ; t始终正常工作):
public Schema getParamSchema() throws ClassNotFoundException {
List<Schema.Field> fields = new ArrayList<>();
fields.add(new Schema.Field("key", Schema.create(Schema.Type.STRING), "Doc: key field", (Object) null));
Schema.Field f = new Schema.Field("type", ReflectData.get().getSchema(ClassUtils.getClass(((Class) this.type).getName())), "Doc: type field", (Object) null);
f.addProp("java-class", ((Class) this.type).getName());
fields.add(f);
fields.add(new Schema.Field("value", ReflectData.get().getSchema(value.getClass()), "Doc: value field", (Object) null));
return Schema.createRecord(((Class) this.type).getName() + "Param", "Doc: param record", this.getClass().getPackage().getName(), false, fields);
}
但是在这种情况下,avro会抛出ClassCastException,因为它不能将Class转换为Boolean,Integer等。我总是在使用avro和java类型和类时遇到很多问题。
所以我认为最好的建议是改变你的模型(Parcel,PluginDump和Param,我的意思),以减少avro的问题。例如,您可以将类型名称存储为字符串,并在反序列化后获取带反射的类型。