Avro中的dymamic Sc​​hemas和嵌套地图

时间:2017-03-07 16:02:23

标签: avro

我是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(????

有什么想法吗?

我觉得我的方法是错误的,但我在动态模式生成或嵌套地图的文档中找不到任何示例。

1 个答案:

答案 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的问题。例如,您可以将类型名称存储为字符串,并在反序列化后获取带反射的类型。