2017-03-07 16 views
0

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

所以當序列化一個包裹時,我該如何'放'每個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

當你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。

然後,我認爲它更好地使用列表而不是地圖,導致avro不能很好地處理具有不同類型的鍵和值的地圖。

關於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公司將不知道它是什麼類型。

如果你想爲每個參數手動生成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異常,因爲它不能施放類布爾,整型等我總是有很多的問題,Avro公司和Java類型和類工作。

所以我認爲最好的建議是改變你的模型(Parcel,PluginDump和Param我的意思是)減少與avro的問題。例如,您可以將類型名稱存儲爲字符串,並在反序列化後獲得帶有反射的類型。