序列化/反序列化spark sql数据帧的现有类

时间:2016-02-05 02:44:46

标签: apache-spark dataframe apache-spark-sql bson

使用spark 1.6.0 说我有这样的课程

case class MyClass(date: java.util.Date, oid: org.bson.types.ObjectId)

如果我有

//rdd: RDD[MyClass]
rdd.toDF("date", "oid")

我得到java.lang.UnsupportedOperationException: Schema for type java.util.Date/org.bson.types.ObjectId is not supported

现在我知道我可以将其设为java.sql.Date,但让我们说MyClass在很多其他地方都依赖于在任何地方做出改变,仍然无法解决ObjectId问题。

我也知道UserDefinedType选项。但似乎只有在你创建一个新的类来使用它时才有效(同样,MyClass的签名需要保持不变)

是不是只能为java.util.Dateorg.bson.types.ObjectId注册一个序列化器/解串器,以便我可以在toDF上调用RDD[MyClass],它会起作用吗?

UPDATE

所以这并没有完全回答我的问题,但它解锁了我们,所以将在这里分享,希望它对其他人有帮助。所以大多数json库都支持这个用例,spark-sql有一个内置的sqlContext.read.json(stringRdd).write.parquet("/path/to/output")。所以你可以使用你选择的json lib定义类的(de)ser,序列化为string,然后spark-sql可以处理其余的

1 个答案:

答案 0 :(得分:0)

这取决于工作的含义。要序列化/反序列化对象,只需要相应的UserDefinedType和正确的注释即可。例如:

@SQLUserDefinedType(udt = classOf[MyClassUDT])
case class MyClass(date: java.util.Date, oid: ObjectId)

class MyClassUDT extends UserDefinedType[MyClass] {
  override def sqlType: StructType = StructType(Seq(
    StructField("date", DateType, nullable = false),
    StructField("oid", StringType, nullable = false)
  ))

  override def serialize(obj: Any): InternalRow = {
    obj match {
      case MyClass(date, oid) =>
        val row = new GenericMutableRow(2)
        row(0) = new java.sql.Date(date.getTime)
        row(1) = UTF8String.fromString(oid.toString)
        row
    }
  }

  override def deserialize(datum: Any): MyClass = {
    datum match {
      case row: InternalRow =>
        val date: java.util.Date = new java.util.Date(
          row.get(0, DateType).asInstanceOf[java.sql.Date].getTime()
        )
        val oid = new ObjectId(row.getString(1))
        MyClass(date, oid)
    }
  }

  override def userClass: Class[MyClass] = classOf[MyClass]
}

这并不意味着您将能够访问在类或其任何字段上定义的任何方法。为此,你需要UDF。

更接近无缝集成的是Spark数据集,但是AFAIK还不能定义自定义编码器。