2016-11-24 68 views
4

我正在和Avro合作,我有一個GenericRecord。我想從中提取clientId,deviceName,holder。在Avro Schema中,clientId是Integer,deviceName是字符串,而holder是Map。如何從GenericRecord獲取類型值?

{ 
    "name" : "clientId", 
    "type" : [ "null", "int" ], 
    "doc" : "hello" 
} 

在Avro的模式deviceName

{ 
    "name" : "deviceName", 
    "type" : [ "null", "string" ], 
    "doc" : "test" 
} 

在Avro的模式holder

在Avro的模式clientId

{ 
    "name" : "holder", 
    "type" : { 
     "type" : "map", 
     "values" : "string" 
    } 
} 

我的問題是 - 什麼是推薦的方式來檢索一個類型值,而不是一個對象?

在下面的代碼中,​​是GenericRecord,我們可以從它得到avro模式。這就是我現在正在做的事情,將所有內容解壓縮爲一個字符串。但是,我怎麼才能得到輸入值。有什麼辦法嗎?我的意思是無論avro模式中的數據類型是什麼,我只想提取它。

public static void getData(GenericRecord payload) { 
    String id = String.valueOf(payload.get("clientId")); 
    String name = String.valueOf(payload.get("deviceName")); 

    // not sure how to get maps here 
    } 

所以我想提取clientId爲整數,deviceName string和holder如Java地圖Map<String, String>GenericRecord?什麼是最好的方式來做到這一點?我們可以編寫任何實用程序,它可以爲通用記錄和架構提供所有類型的轉換嗎?

+0

你的三個'payload.get'調用返回什麼類型的對象? – qxz

+0

@qxz的含義?我不明白。它將所有東西都作爲一個對象返回,然後你必須將它轉換成它的樣子。我想我們可以使用有效載荷的模式並編寫一些實用程序來轉換並返回結果。 – john

+0

對於每一個,嘗試打印'payload.get(「...」)。getClass()。getCanonicalName()'查看對象的類型 – qxz

回答

1

你應該能夠投你stringUtf8intInteger,並mapMap<Utf8, Utf8>。這應該沒有引起ClassCastException工作:

public static void getData(GenericRecord payload) { 
    int id = (Integer) payload.get("clientId"); 
    String name = payload.get("deviceName").toString(); // calls Utf8.toString 
    Map<Utf8, Utf8> holder = (Map<Utf8, Utf8>) payload.get("holder"); 

    ... 
} 

一般情況下,我相信你能做到這些類型轉換:

  • 元成爲其盒裝版本(IntegerDouble等)
  • stringUtf8
  • bytes變成java.nio.ByteBuffer
  • array成爲java.util.Collection
  • map成爲java.util.Map<Utf8, [value type]>
1

你可以試試這個方法。爲了強大的實施,你應該考慮代碼generation using schema compilation.

package stackoverflow; 

import static org.hamcrest.CoreMatchers.is; 
import static org.junit.Assert.assertThat; 

import java.util.Arrays; 
import java.util.HashMap; 
import java.util.Map; 

import org.apache.avro.AvroTypeException; 
import org.apache.avro.Schema; 
import org.apache.avro.Schema.Field; 
import org.apache.avro.Schema.Type; 
import org.apache.avro.generic.GenericData.Record; 
import org.apache.avro.generic.GenericRecord; 
import org.apache.avro.util.Utf8; 
import org.junit.Test; 

// Just for demonistration; not robust implementation 
public class GenericRecordType { 
    @Test 
    public void testName() throws Exception { 
     Schema schema = buildSchema(); 

     GenericRecord record = new Record(schema); 
     record.put("clientId", 12); 
     record.put("deviceName", "GlassScanner"); 
     record.put("holder", new HashMap<>()); 

     Integer value = IntField.clientId.getValue(record); 
     String deviceName = StringField.deviceName.getValue(record); 
     Map<String, String> mapString = MapOfStringField.holder.getValue(record); 

     assertThat(deviceName, is("GlassScanner")); 
     assertThat(value, is(12)); 
     assertThat(mapString.size(), is(0)); 
    } 

    private Schema buildSchema() { 
     Field clientId = new Field("clientId", Schema.create(Type.INT), "hello", (Object) null); 
     Field deviceName = new Field("deviceName", Schema.create(Type.STRING), "hello", (Object) null); 
     Field holder = new Field("holder", Schema.createMap(Schema.create(Type.STRING)), null, (Object) null); 
     Schema schema = Schema.createRecord(Arrays.asList(clientId, deviceName, holder)); 
     return schema; 
    } 

    public static interface TypedField<T> { 
     String name(); 

     public T getValue(GenericRecord record); 

    } 

    public static enum StringField implements TypedField<String> { 
     deviceName; 

     @Override 
     public String getValue(GenericRecord record) { 
      String typed = null; 
      Object raw = record.get(name()); 
      if (raw != null) { 
       if (!(raw instanceof String || raw instanceof Utf8)) { 
        throw new AvroTypeException("string type was epected for field:" + name()); 
       } 
       typed = raw.toString(); 
      } 
      return typed; 
     } 

    } 

    public static enum IntField implements TypedField<Integer> { 
     clientId; 

     private IntField() { 
     } 

     @Override 
     public Integer getValue(GenericRecord record) { 
      Integer typed = null; 
      Object raw = record.get(name()); 
      if (raw != null) { 
       if (!(raw instanceof Integer)) { 
        throw new AvroTypeException("int type was epected for field:" + name()); 
       } 
       typed = (Integer) raw; 
      } 
      return typed; 
     } 

    } 

    public static enum MapOfStringField implements TypedField<Map<String, String>> { 
     holder; 

     @Override 
     @SuppressWarnings("unchecked") 
     public Map<String, String> getValue(GenericRecord record) { 
      Map<String, String> typed = null; 
      Object raw = record.get(name()); 
      if (raw != null) { 
       if (!(raw instanceof Map)) { 
        throw new AvroTypeException("map type was epected for field:" + name()); 
       } 
       typed = (Map<String, String>) raw; 
      } 
      return typed; 
     } 
    } 

}