首页 文章

如何使用在Scala中创建数据集的通用案例类实现特征

提问于
浏览
2

我想创建一个应该用案例类T实现的Scala特征 . 特性只是加载数据并将其转换为类型为T的Spark数据集 . 我得到的错误是没有编码器可以存储,我认为是因为Scala不知道T应该是一个案例类 . 我该如何告诉编译器?我已经看到某个地方我应该提到Product,但是没有定义这样的类...请随意提出其他方法来做到这一点!

我有以下代码,但它没有编译错误:42:错误:无法找到存储在数据集中的类型的编码器 . 导入sqlContext.implicits._ [INFO] .as [T]支持原始类型(Int,String等)和产品类型(case类)

我正在使用Spark 1.6.1

码:

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.{Dataset, SQLContext}    

/**
      * A trait that moves data on Hadoop with Spark based on the location and the granularity of the data.
      */
    trait Agent[T] {
      /**
        * Load a Dataframe from the location and convert into a Dataset
        * @return Dataset[T]
        */
      protected def load(): Dataset[T] = {
        // Read in the data
        SparkContextKeeper.sqlContext.read
          .format("com.databricks.spark.csv")
          .load("/myfolder/" + location + "/2016/10/01/")
          .as[T]
      }
    }

2 回答

  • 0

    您的代码缺少3件事:

    • 确实,你必须让编译器知道T是 Product 的子类(所有Scala案例类和元组的超类)

    • 编译器还需要实际案例类的 TypeTagClassTag . Spark隐式使用它来克服类型擦除

    • 导入 sqlContext.implicits._

    不幸的是,您无法在特征中添加带有上下文边界的类型参数,因此最简单的解决方法是使用 abstract class 代替:

    import scala.reflect.runtime.universe.TypeTag
    import scala.reflect.ClassTag
    
    abstract class Agent[T <: Product : ClassTag : TypeTag] {
      protected def load(): Dataset[T] = { 
        val sqlContext: SQLContext = SparkContextKeeper.sqlContext
        import sqlContext.implicits._
        sqlContext.read.// same... 
      }
    }
    

    显然,这不是最适合这份工作的 . 另一种方法是将 load 放在一个对象中并将type参数移动到该方法:

    object Agent {
      protected def load[T <: Product : ClassTag : TypeTag](): Dataset[T] = {
        // same...
      }
    }
    

    哪一个更可取的主要取决于您将在何处以及如何调用 load 以及您计划对结果做什么 .

  • 4

    你需要采取两个行动:

    • 在您的导入中添加 import sparkSession.implicits._

    • 让你的特质 trait Agent[T <: Product]

相关问题