2016-11-24 119 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; 
     } 
    } 

}