web-dev-qa-db-ja.com

GenericRecordから型付きの値を取得するにはどうすればよいですか?

私は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"
    }
}

私の質問は、オブジェクトではなく、型付きの値を取得するための推奨される方法は何ですか?

以下のコードでは、payloadGenericRecordであり、そこから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を文字列、holderをJava map Map<String, String> from GenericRecord?それを行うための最良の方法は何ですか?一般的なレコードとスキーマを指定して、型指定されたすべての変換を実行するユーティリティを作成できますか?

8
john

stringの値をUtf8に、intIntegerに、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になります
  • bytesJava.nio.ByteBufferになります
  • arrayJava.util.Collectionになります
  • mapJava.util.Map<Utf8, [value type]>になります
4
qxz

このアプローチを試すことができます。堅牢な実装のために、コードを検討する必要があります スキーマコンパイルを使用した生成

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;
        }
    }

}
1
skadya