【问题标题】:dymamic Schemas and nested Maps in AvroAvro 中的动态模式和嵌套映射
【发布时间】:2017-03-07 16:02:23
【问题描述】:

我是 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。

那么在序列化 Parcel 时,我如何“放置”每个 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(????

有什么想法吗?

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

【问题讨论】:

    标签: avro


    【解决方案1】:

    1 当您收到GenericRecord parcelRecord = new GenericData.Record(parcelSchema); 时,您的记录中有两个字段:recipe 和 dumps,因此您无法遍历转储,您必须将准备好的带有转储的地图放在第二个字段中记录在案,就像你为食谱所做的那样:parcelRecord.put("dumps", dumps);。但是在这种情况下,你会得到 ClassCastException,因为 PluginDump 不能被强制转换为 org.apache.avro.generic.IndexedRecord,所以你需要在 parcelRecord 中放入一个 GenericRecords 的 Map。 Map&lt;String, Param&gt; params 也需要这个,因为 Param 也不能转换为 IndexedRecord。

    2然后,我认为最好使用 Lists 而不是 Maps,因为 avro 不能很好地处理具有不同类型的键和值的 Maps。

    3关于Param类:如果你要使用自动生成的schema,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不知道是什么类型。

    如果你想为每个参数手动生成 avro-schema,考虑到它的类型,你可以这样做(我使用了来自 apache commons-lang3 的 ClassUtils.getClass,因为标准的 Class.forName 方法没有始终正常工作):

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-12
      • 1970-01-01
      • 2013-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-02
      • 2016-09-13
      相关资源
      最近更新 更多