如何避免在avro中从GenericRecord中提取字段时进行转换?

时间:2016-12-23 19:48:32

标签: java casting type-conversion avro

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

以下是完成所有工作的方法:

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

以下是我使用上述extract方法从GenericRecord对象中提取单个字段的方法。现在我只是提取String Integer数据类型字段,但我可能还需要提取其他数据类型字段。

现在我的问题是。有什么方法可以避免转换,因为现在我在从中提取字段时转换每种数据类型。我认为每当有演员表时,必须有更好的方式做可以避免演员的事情。

Map<String, String> payload = (Map<String, String>) extract(genericRecord, "payload");
String clientId = (String) extract(genericRecord, "clientId");
Integer deviceId = (Integer) extract(genericRecord, "deviceId");

1 个答案:

答案 0 :(得分:0)

  

现在我的问题是。我有什么方法可以避免施放,因为正确   现在我在从中提取字段时转换每种数据类型。一世   想想只要有演员,必须有更好的办法   这可以避免施法。

您使用非常通用的方法,因为Avro的GenericRecord是一个非常通用的地图(它使用Object作为键,Object作为值。) 在您的情况下,由于您使用的是非常弱类型的数据:Object类,因此很遗憾,一次或另一次无法进行转换。 现在,您可以在实用程序类中而不是在客户端中创建它。 它会减少工作的痛苦,但在运行时不会更安全。

您可以通过以下方式更改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;
   }

现在在客户端你可以这样做:

Map<String, String> payload = extract(genericRecord, "payload");
String clientId = extract(genericRecord, "clientId");
Integer deviceId = extract(genericRecord, "deviceId");