具有转义字段名称的 Scala 案例 class 在 Spark Catalyst 代码生成期间抛出错误

Scala case class with escaped field name throws error during Spark Catalyst code generation

我有一个案例 class 带有转义的字段名称,例如:

case class Buyer(`52_week`: String, `26_week`: String,... other fields)

然后我创建这个 class 的一个实例并像这样传递给 Spark DataFrame:

val expected = spark.createDataFrame(sc.parallelize(List(buyer1, buyer2)))

当我尝试保存它时:

expected.write.mode(SaveMode.Append).format("hive").partitionBy("load_date").saveAsTable(tableName)

我得到以下异常:

Caused by: org.codehaus.commons.compiler.CompileException: File 'generated.java', Line 476, Column 15: failed to compile: org.codehaus.commons.compiler.CompileException: File 'generated.java', Line 476, Column 15: Expression "funcResult15 = value65" is not a type

第 476 行是:

/* 475 */           Object funcResult15 = null;
/* 476 */           funcResult15 = value65.52_week();

看来我需要以某种方式告诉 Catalyst codegen 转义这些字段。

有没有办法解决这个问题,或者至少有一些解决方法?

我正在使用 Apache Spark 2.2.0。

我建议你做一个不同的case class,用一个构造函数从旧的构造函数构建一个对象:

case class BuyerRenamed(week_52: String, week_26: String,... other fields)
object BuyerRenamed {
  def apply(buyer: Buyer): BuyerRenamed = {
    BuyerRenamed(buyer.`52_week`, buyer.`26_week`, ...)
  }
}

然后将新案例 class 与数据框一起使用,这将解决 Catalyst 的问题,因为字段将被命名为 value65.week_52(),这是对方法的适当命名。
也许您会想要添加隐式转换以使代码更简洁。

在很长的运行中,我建议您重构现有代码以摆脱这种命名,它可能会导致各种突发错误,尤其是当您以某种方式连接到自动代码生成时。

我所做的是将 Buyer class 转换为元组,并明确说明要使用的列名。

换句话说,我通过选择不同的路径来绕过代码生成。

代码片段如下所示:

val expected = spark.createDataFrame(sc.parallelize(List(buyer1, buyer2).map(b => Brand.unapply(b).get))).toDF(extractFieldNames[Buyer]:_*)

其中 extractFieldNames 是:

def extractFieldNames[T<:Product](implicit m: Manifest[T]): Array[String] =
    m.erasure.getDeclaredFields.map(_.getName)