我正在将 Spark 数据帧保存为镶木地板文件,并且数据帧具有从 avro 对象构建的行。完全相同的确切代码在这里 - https://stackoverflow.com/a/41491999/2440775
我面临的挑战是,当传入数据中缺少整数字段时,我打算能够具有空值。Avro 似乎通过使用联合类型允许这样做,但是当我没有在 avsc 中指定默认值或指定默认值"null"时,我会收到如下错误:
Caused by: org.apache.avro.AvroRuntimeException: Field xxx type:LONG pos:7 not set and has no default value
at org.apache.avro.generic.GenericData.getDefaultValue(GenericData.java:984)
at org.apache.avro.data.RecordBuilderBase.defaultValue(RecordBuilderBase.java:135)
Or
Caused by: org.apache.avro.AvroRuntimeException: Field xxx type:UNION pos:7 not set and has no default value
at org.apache.avro.generic.GenericData.getDefaultValue(GenericData.java:984)
at org.apache.avro.data.RecordBuilderBase.defaultValue(RecordBuilderBase.java:135)
如果我写默认值"0",它保存AsParquet效果很好
我还尝试将 avro 规范更改为首先具有"null"类型,因为联合选择第一个元素的类型。
"type": ["null","long"], "default": null
这会导致异常,如下所示:
org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 0.0 failed 1 times, most recent failure: Lost task 0.0 in stage 0.0 (TID 0, localhost): java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
更改 avro 架构中长整型和 null 的顺序会导致以下异常
由以下原因引起:org.apache.avro.AvroType异常:长整型的非数字默认值:null
这样的解决方案,但找到了解决方法。我从 avro 对象构建 Row 的方式是从 avro 对象创建一个列表,然后对其执行 Row.fromSeq。解决方法检查默认值 0 和数据类型为 int 或 long。在默认值的情况下,改为添加 null。因此,在选择默认值时必须小心。
public static List avroToList(AvroData a) throws UnsupportedEncodingException{
List l = new ArrayList<>();
for (Schema.Field f : a.getSchema().getFields()) {
Object value = a.get(f.name());
if (value == null) {
l.add(null);
}
else {
switch (f.schema().getType().getName()){
case "union":
l.add(value.toString());
break;
case "int":
if(value == 0) {l.add(null);}
else {l.add(Integer.valueOf(value.toString()));}
break;
case "long":
if(value == 0L) {l.add(null);}
else {l.add(Long.valueOf(value.toString()));}
break;
default:l.add(value);
break;
}
}
}
return l;
}
该 avsc 文件具有如下类型信息
"type": "long", "default": 0