我正在与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");
答案 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");