我如何处理下面的udf中的空指针异常错误

时间:2018-06-13 14:05:58

标签: scala

我正在尝试在下面的df上运行udf udfTest,但它正在运行 空指针异常可能是由于date1列中的空值。下面的代码需要进行哪些更改才能处理此异常?

 +-----------+----------------+----------+
    |info       |date1           | date2    |
    +-----------+----------------+----------+
    |      11111|      2018-04-13|2018-05-20|
    |      11111|                |2018-05-20|
    |      22222|      2018-05-01|2018-05-18|
    |      22222|                |2018-05-20|
    |      33333|      2020-05-03|2018-05-18|
    |      33333|                |2017-04-17|
    |      12931|      2018-05-07|2018-05-07|
    |      12931|      2018-05-03|2018-05-04|
    |      12931|      2018-05-20|2018-05-26|
    |      12931|      2008-05-03|2018-05-20|
    +-----------+----------------+----------+

def get_value(info: String, date1: String, date2: String): String = {
    var str1: String = null
    if (info == "11111" && date1 != null) {
      str1 = date1
    } else if (info == "22222" && date1 != null) {
      str1 = date1
    } else if (info == "33333" && date2 != null) {
      str1 = date2
    } else
      str1 = null
    str1
  }


  val udfTest = udf((id: String, date1: String, date2: String) => {
    get_value(id: String, date1: String, date2: String)
  })
  df.withColumn("date3", udfTest(df("info"),df("date1"), df("date2")))

错误:

Caused by: java.lang.NullPointerException
  at java.lang.String.<init>(String.java:152)
  at get_value(<console>:61)

4 个答案:

答案 0 :(得分:4)

您可以考虑使用Option&to;来封装空值。在scala代码中散落的空洞通常被认为是代码气味。或许,这种效果可能有所帮助。

val getValue: (String, String, String) => Option[String] = { (info, date1, date2) =>
  (info, Option(date1), Option(date2)) match {
    case ("11111", Some(d1), _) => Some(d1)
    case ("22222", Some(d1), _) => Some(d1)
    case ("33333", _, Some(d2)) => Some(d2)
    case _ => None
  }
}

然后您可以在UDF中使用它,如下所示,

val udfTest = udf(getValue)
df.withColumn("optional", udfTest(df("info"), df("date1"), df("date2"))).show()

请注意,现在而不是String列,您的数据框中有一个Option [String]列。

Here's scala中可选数据类型的有趣指南,以及有趣的用例。

编辑:

这样的事情可以解决你关于翻译假日spark sql代码的问题

private val yyyyMMddFormat = new SimpleDateFormat("yyyy-MM-dd")
private val days = List("Saturday", "Sunday")
private val holidays = List(
  getPreviousDay("2018-05-22"), 
  getPreviousDay("2018-06-01")
)

def isPreviousDayAHoliday(date: String, days: List[String], holidays: List[java.sql.Date]): Boolean = {
  val previousDay = getPreviousDay(date)
  val eeeeFormat = new SimpleDateFormat("EEEE")
  val dayOfPreviousDay = eeeeFormat.format(previousDay)
  days.contains(dayOfPreviousDay) || holidays.contains(previousDay)
}

def getPreviousDay(date: String): java.sql.Date = {
  new java.sql.Date(yyyyMMddFormat.parse(date).getTime - DAYS.toMillis(1))
}

现在你可以用这个作为后卫了。

(info, Option(date1), Option(date2)) match {
  case ("12391", _, Some(d2)) if isPreviousDayAHoliday(d2, days, holidays) => Some(yyyyMMddFormat.format(getPreviousDay(d2)))
  case _ => None
}

希望这有帮助

答案 1 :(得分:2)

像往常一样,当有内置功能的替代品时,我总是建议不要使用 udf函数。由于udf函数需要对每一行进行序列化和反序列化,因此它不如内置函数有效。

您可以使用when/otherwiseisnullnot内置函数来实现您的要求

import org.apache.spark.sql.functions._
df.withColumn("date3",
  when(not(isnull(col("date1"))) && col("info") === "11111", col("date1")).otherwise(
  when(not(isnull(col("date1"))) && col("info") === "22222", col("date1")).otherwise(
    when(not(isnull(col("date2"))) && col("info") === "33333", col("date2")).otherwise(lit(null))
  )
  )
)

或者只使用when/otherwiseisNotNull函数作为

import org.apache.spark.sql.functions._
df.withColumn("date3",
  when(col("date1").isNotNull && col("info") === "11111", col("date1")).otherwise(
  when(col("date1").isNotNull && col("info") === "22222", col("date1")).otherwise(
    when(col("date2").isNotNull && col("info") === "33333", col("date2")).otherwise(lit(null))
  )
  )
)

答案 2 :(得分:1)

为了防止空指针异常,您可以将String常量与info变量进行比较,而不是相反。你真的不需要str1变量。至少与您的建议不同的工作解决方案是:

def get_value(info: String, date1: String, date2: String): String = {
  if ("11111" == info && date1 != null) {
    date1
  } else if ("22222" == info && date1 != null) {
    date1
  } else if ("33333" == info && date2 != null) {
    date2
  } else
    "null"
}

答案 3 :(得分:0)

使用Option让它发挥作用。

   def get_value(info: String, date1: String, date2: String): String = {
    var str1: String = null
  if ("11111" == info && date1 != null) {
    Option(tdate1).getOrElse("null")
  } else if ("22222" == info && date1 != null) {
    Option(tdate1).getOrElse("null")
  } else if ("33333" == info && date2 != null) {
    Option(tdate2).getOrElse("null")
  } else
    Option(str1).getOrElse("null")
}