Java 在avro中从GenericRecord提取字段时如何避免强制转换?

Java 在avro中从GenericRecord提取字段时如何避免强制转换?,java,casting,type-conversion,avro,Java,Casting,Type Conversion,Avro,我正在与Avro合作,并试图以一种通用的方式从中提取几个字段。我有一个来自avro的对象,我想从中提取几个字段 以下是所有操作的方法: public static Object extract(GenericRecord genericRecord, String fieldName) { Object result = new Object(); for (Field field : genericRecord.getSchema().getFields()) {

我正在与Avro合作,并试图以一种通用的方式从中提取几个字段。我有一个来自avro的对象,我想从中提取几个字段

以下是所有操作的方法:

  public static Object extract(GenericRecord genericRecord, String fieldName) {
    Object result = new Object();
    for (Field field : genericRecord.getSchema().getFields()) {
      if (field.name().equalsIgnoreCase(fieldName))
        return fromAvro(genericRecord.get(field.name()), field.schema());
    }
    return result;
  }

  private static Object fromAvro(Object obj, Schema schema) {
    if (obj == null)
      return null;
    switch (schema.getType()) {
      case UNION:
        return fromAvroUnion(obj, schema);
      case ARRAY:
        return fromAvroArray(obj, schema);
      case STRING:
        if (obj.equals("null") || obj.toString().equals("null"))
          return null;
        else
          return obj.toString();
      case MAP:
        return fromAvroMap(obj, schema);
      case NULL:
        return obj;
      case BOOLEAN:
        return Boolean.valueOf(obj.toString());
      case DOUBLE:
        return Double.valueOf(obj.toString());
      case FLOAT:
        return Float.valueOf(obj.toString());
      case INT:
        return Integer.valueOf(obj.toString());
      case LONG:
        return Long.valueOf(obj.toString());
    }
  }

  private static Object fromAvroArray(Object obj, Schema schema) {
    List<Object> array = new ArrayList<Object>();
    for (Object element : (GenericData.Array) obj) {
      array.add(fromAvro(element, schema.getElementType()));
    }
    return array;
  }

  private static Object fromAvroMap(Object obj, Schema schema) {
    Map<String, Object> convertedMap = new HashMap<>();
    // CharSequence because the string can be configured as either Utf8 or String.
    for (Entry<CharSequence, Object> e : ((Map<CharSequence, Object>) obj).entrySet()) {
      convertedMap.put(e.getKey().toString(), fromAvro(e.getValue(), schema.getValueType()));
    }
    return convertedMap;
  }

  private static Object fromAvroUnion(Object obj, Schema schema) {
    List<Schema> types = schema.getTypes();
    if (types.size() < 1) {
      throw new AvroRuntimeException("Union has no types");
    }
    if (types.size() == 1) {
      return fromAvro(obj, types.get(0));
    } else if (types.size() > 2) {
      throw new AvroRuntimeException(
          "Unions may only consist of a concrete type and null in cascading.avro");
    } else if (!types.get(0).getType().equals(Type.NULL)
        && !types.get(1).getType().equals(Type.NULL)) {
      throw new AvroRuntimeException(
          "Unions may only consist of a concrete type and null in cascading.avro");
    } else {
      Integer concreteIndex = (types.get(0).getType() == Type.NULL) ? 1 : 0;
      return fromAvro(obj, types.get(concreteIndex));
    }
  }
现在我的问题是。我有没有办法避免选角,因为对吗 现在,我正在强制转换每个数据类型,同时从中提取字段。我 想一想,只要有演员阵容,就一定有更好的方式来做事情 这可以避免铸造

Map<String, String> payload = (Map<String, String>) extract(genericRecord, "payload");
String clientId = (String) extract(genericRecord, "clientId");
Integer deviceId = (Integer) extract(genericRecord, "deviceId");
您使用非常通用的方法,因为来自Avro的
genericord
是一个非常通用的映射(它使用
Object
作为键,
Object
作为值)。
在您的情况下,不幸的是,在某个时间或另一个时间强制转换是不可避免的,因为您使用的是非常弱类型的数据:
Object
class.
现在,您可以在实用程序类而不是客户端中实现它。 这将使工作不那么痛苦,但在运行时不会更安全

您可以通过以下方式更改
extract()
方法:

   @SuppressWarnings("unchecked")
   public static <T> T extract(GenericRecord genericRecord, String fieldName) {
    for (Field field : genericRecord.getSchema().getFields()) {
      if (field.name().equalsIgnoreCase(fieldName))
        return (T) fromAvro(genericRecord.get(field.name()), field.schema());
    }
    return null;
   }
@SuppressWarnings(“未选中”)
公共静态T提取(GenericRecord GenericRecord,字符串字段名){
for(字段:genericRecord.getSchema().getFields()){
if(field.name().equalsIgnoreCase(fieldName))
从avro(genericord.get(field.name()),field.schema())返回(T);
}
返回null;
}
现在在客户端,您可以执行以下操作:

Map<String, String> payload = extract(genericRecord, "payload");
String clientId = extract(genericRecord, "clientId");
Integer deviceId = extract(genericRecord, "deviceId");
Map payload=extract(通用记录,“payload”);
字符串clientId=extract(genericord,“clientId”);
整数设备ID=提取(genericRecord,“设备ID”);

它不工作了。我试着改变你的建议。它正在抱怨所有switch语句的情况。字符串大小写的一个示例错误是
类型不匹配:无法从字符串转换为T
。您还应该强制转换为
(T)
您调用的所有方法的返回,以及当您在
开关中返回NOTNULL值时。我编辑以说明。我还在方法上添加了
@SuppressWarnings(“unchecked”)
。我还必须更改
提取方法吗?对的因为它给了我一个错误。我更新了我的答案。最后,您只需要在更高级别的调用中引入
T
generic,即
extract()
方法,并将
fromAvro()
方法的返回转换为
T
。此外,我认为
对象结果
变量不是必需的,因为如果架构中没有匹配的字段,您应该返回一个
null
值。为什么不使用
SpecificRecord