Spark:将字符串转换为日期时出现ClassCastException

时间:2017-02-27 16:31:21

标签: scala date apache-spark

我正在尝试使用以下代码阅读数据框中的dict1 = {1:'True', 2:'True', 3:'False', 4:'False', 5:'True'} list = [] for k, v in dict1.items(): if v == 'True': #print(k, sep=' ', end=',') list.append(k) print(list) 文件:

parquet

这是架构:

val data = spark.read.schema(schema)
        .option("dateFormat", "YYYY-MM-dd'T'hh:mm:ss").parquet(<file_path>)

data.show()

当我尝试执行def schema: StructType = StructType(Array[StructField]( StructField("id", StringType, false), StructField("text", StringType, false), StructField("created_date", DateType, false) )) 时,它会抛出以下异常:

data.show()

显然,这是因为我的架构中的日期格式和Caused by: java.lang.ClassCastException: [B cannot be cast to java.lang.Integer at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101) at org.apache.spark.sql.catalyst.expressions.MutableInt.update(SpecificInternalRow.scala:74) at org.apache.spark.sql.catalyst.expressions.SpecificInternalRow.update(SpecificInternalRow.scala:240) at org.apache.spark.sql.execution.datasources.parquet.ParquetRowConverter$RowUpdater.set(ParquetRowConverter.scala:159) at org.apache.spark.sql.execution.datasources.parquet.ParquetPrimitiveConverter.addBinary(ParquetRowConverter.scala:89) at org.apache.parquet.column.impl.ColumnReaderImpl$2$6.writeValue(ColumnReaderImpl.java:324) at org.apache.parquet.column.impl.ColumnReaderImpl.writeCurrentValueToConverter(ColumnReaderImpl.java:372) at org.apache.parquet.io.RecordReaderImplementation.read(RecordReaderImplementation.java:405) at org.apache.parquet.hadoop.InternalParquetRecordReader.nextKeyValue(InternalParquetRecordReader.java:198) 。如果我将DateType更改为DateType,则可以正常工作并输出以下内容:

StringType

我想将+--------------------+--------------------+----------------------+ | id| text| created_date| +--------------------+--------------------+----------------------+ |id..................|text................|2017-01-01T00:08:09Z| 读入created_date,我是否需要更改其他内容?

1 个答案:

答案 0 :(得分:0)

以下在Spark 2.1下工作。请注意日期格式的更改以及TimestampType的使用而不是DateType。

val schema = StructType(Array[StructField](
  StructField("id", StringType, false),
  StructField("text", StringType, false),
  StructField("created_date", TimestampType, false)
))

val data = spark
  .read
  .schema(schema)
  .option("dateFormat", "yyyy-MM-dd'T'HH:mm:ss'Z'")
  .parquet("s3a://thisisnotabucket")

在旧版本的Spark中(我可以确认这在1.5.2下工作),您可以创建一个UDF来在SQL中为您进行转换。

def cvtDt(d: String): java.sql.Date = { 
  val fmt = org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
  new java.sql.Date(fmt.parseDateTime(d).getMillis)
}

def cvtTs(d: String): java.sql.Timestamp = {
  val fmt = org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
  new java.sql.Timestamp(fmt.parseDateTime(d).getMillis)
}

sqlContext.udf.register("mkdate", cvtDt(_: String))
sqlContext.udf.register("mktimestamp", cvtTs(_: String))

sqlContext.read.parquet("s3a://thisisnotabucket").registerTempTable("dttest")

val query = "select *, mkdate(created_date), mktimestamp(created_date) from dttest"
sqlContext.sql(query).collect.foreach(println)

注意:我在REPL中执行了此操作,因此我必须在每次调用cvt *方法时创建DateTimeFormat模式以避免序列化问题。如果您正在执行此操作是应用程序,我建议将格式化程序提取到对象中。

object DtFmt {
  val fmt = org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd'T‌​'HH:mm:ss'Z'")
}