从行中读取列时出现空指针异常。

4
以下是读取 Row 中值的 Scala 代码(Spark 1.6),当该值为空时,会出现 NullPointerException。
val test = row.getAs[Int]("ColumnName").toString

虽然这样可以正常工作

val test1 = row.getAs[Int]("ColumnName") // returns 0 for null
val test2 = test1.toString // converts to String fine

什么导致了NullPointerException并建议如何处理这种情况?
PS:从DataFrame中获取行的方式如下:
val myRDD = myDF.repartition(partitions)
  .mapPartitions{ rows => 
    rows.flatMap{ row =>
      functionWithRows(row) //has above logic to read null column which fails
    }
  }

functionWithRows遇到了上述的NullPointerException错误。

我的数据框架(MyDF):

root
 |-- LDID: string (nullable = true)
 |-- KTAG: string (nullable = true)
 |-- ColumnName: integer (nullable = true)

你能否编辑你的问题并添加整个堆栈跟踪信息?最近版本的Spark 2.3.0-SNAPSHOT中似乎无法复现这个问题。 - Jacek Laskowski
@JacekLaskowski:我已经将我的生产代码抽象出来。在使用Spark 1.6.1时,以下行会导致NullPointerException: val test = row.getAsInt.toString - Anurag Sharma
1
@JacekLaskowski 这段代码会抛出 NPE:spark.sql(" select 1 as col union all select null as col").map(_.getAs[Int]("col").toString ).collect。去掉 toString 就可以正常运行了。 - philantrovert
2个回答

2

getAs 的定义如下:

def getAs[T](i: Int): T = get(i).asInstanceOf[T]

当我们执行toString时,调用的是Object.toString,它不依赖于类型,因此asInstanceOf[T]会被编译器忽略。

row.getAs[Int](0).toString -> row.get(0).toString

我们可以通过编写简单的Scala代码来确认这一点:
import org.apache.spark.sql._

object Test {
  val row = Row(null)
  row.getAs[Int](0).toString
}

然后进行编译:

$ scalac -classpath $SPARK_HOME/jars/'*' -print test.scala
[[syntax trees at end of                   cleanup]] // test.scala
package <empty> {
  object Test extends Object {
    private[this] val row: org.apache.spark.sql.Row = _;
    <stable> <accessor> def row(): org.apache.spark.sql.Row = Test.this.row;
    def <init>(): Test.type = {
      Test.super.<init>();
      Test.this.row = org.apache.spark.sql.Row.apply(scala.this.Predef.genericWrapArray(Array[Object]{null}));
      Test.this.row().getAs(0).toString();
      ()
    }
  }
}

所以正确的方式应该是:
String.valueOf(row.getAs[Int](0))

1
为了避免空值,最好的做法是在检查之前使用isNullAt,正如文档所建议的那样:

getAs

<T> T getAs(int i)

返回位置i处的值。对于原始类型,如果值为null,则返回特定于原始类型的“零值”,即Int0 - 使用isNullAt确保值不为空

我同意这种行为令人困惑。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接