如何从GenericRecord获取类型值?

时间:2016-11-24 18:18:04

标签: java avro

我正在与Avro合作,我有GenericRecord。我想从中提取clientIddeviceNameholder。在Avro架构中,clientId为整数,deviceName为字符串,holder为地图。

avro架构中的

clientId

{
    "name" : "clientId",
    "type" : [ "null", "int" ],
    "doc" : "hello"
}
avro架构中的

deviceName

{
    "name" : "deviceName",
    "type" : [ "null", "string" ],
    "doc" : "test"
}
avro架构中的

holder

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

我的问题是 - 检索键入值的推荐方法是什么,而不是Object?

在下面的代码中,payloadGenericRecord,我们可以从中获取avro架构。这就是我现在正在做的事情,将所有内容都作为String提取。但是我怎样才能获得输入值。有什么办法吗?我的意思是无论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为整数,holder为字符串,Map<String, String>为Java地图GenericRecord?最好的方法是什么?我们可以编写任何执行所有类型转换的实用程序给出通用记录和模式吗?

2 个答案:

答案 0 :(得分:3)

您应该能够将string值转换为Utf8,将int转换为Integer,将map转换为Map<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等。)
  • string变为Utf8
  • bytes变为java.nio.ByteBuffer
  • array变为java.util.Collection
  • map变为java.util.Map<Utf8, [value type]>

答案 1 :(得分: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;
        }
    }

}