很抱歉提出一个简单的问题。我想将case类传递给函数参数,并且想在函数内部进一步使用它。到目前为止,我已经使用TypeTag
和ClassTag
进行了尝试,但是由于某种原因,我无法正确使用它,或者可能是因为我不在寻找正确的位置。
用例与此类似:
case class infoData(colA:Int,colB:String)
case class someOtherData(col1:String,col2:String,col3:Int)
def readCsv[T:???](path:String,passedCaseClass:???): Dataset[???] = {
sqlContext
.read
.option("header", "true")
.csv(path)
.as[passedCaseClass]
}
它会被这样称呼:
val infoDf = readCsv("/src/main/info.csv",infoData)
val otherDf = readCsv("/src/main/someOtherData.csv",someOtherData)
答案 0 :(得分:3)
首先将函数定义更改为:
object t0 {
def readCsv[T] (path: String)(implicit spark: SparkSession, encoder: Encoder[T]): Dataset[T] = {
spark
.read
.option("header", "true")
.csv(path)
.as[T]
}
}
您无需执行任何类型的反射即可创建通用的readCsv函数。这里的关键是Spark在编译时需要编码器。因此,您可以将其作为隐式参数传递,编译器将添加它。
由于Spark SQL可以反序列化包括默认编码器在内的产品类型(您的案例类),因此很容易调用以下函数:
case class infoData(colA: Int, colB: String)
case class someOtherData(col1: String, col2: String, col3: Int)
object test {
import t0._
implicit val spark = SparkSession.builder().getOrCreate()
import spark.implicits._
readCsv[infoData]("/tmp")
}
希望有帮助
答案 1 :(得分:3)
您应该注意两件事,
CamelCase
中,所以InfoData
。DataSet
后,就不会将其绑定到DataFrame
。 DataFrame
是通用DataSet
的{{1}}的特殊名称。您需要确保所提供的类在当前范围内具有对应的Row
的隐式实例。
Encoder
可以通过导入case class InfoData(colA: Int, colB: String)
获得原始类型(Encoder
,Int
等)和String
的 case classes
实例
spark.implicits._
或者,您可以使用上下文绑定
def readCsv[T](path: String)(implicit encoder: Encoder: T): Dataset[T] = {
spark
.read
.option("header", "true")
.csv(path)
.as[T]
}
现在,您可以按以下方式使用它,
def readCsv[T: Encoder[T]](path: String): Dataset[T] = {
spark
.read
.option("header", "true")
.csv(path)
.as[T]
}