Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala Spark:将字符串转换为日期时发生ClassCastException_Scala_Date_Apache Spark - Fatal编程技术网

Scala Spark:将字符串转换为日期时发生ClassCastException

Scala Spark:将字符串转换为日期时发生ClassCastException,scala,date,apache-spark,Scala,Date,Apache Spark,我正在使用以下代码尝试读取数据框中的parquet文件: val data = spark.read.schema(schema) .option("dateFormat", "YYYY-MM-dd'T'hh:mm:ss").parquet(<file_path>) data.show() 当我尝试执行data.show()时,它抛出以下异常: Caused by: java.lang.ClassCastException: [B cannot be cast t

我正在使用以下代码尝试读取数据框中的
parquet
文件:

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

data.show()
当我尝试执行
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
更改为
StringType
,它工作正常并输出以下内容:

+--------------------+--------------------+----------------------+
|                  id|                text|          created_date|
+--------------------+--------------------+----------------------+
|id..................|text................|2017-01-01T00:08:09Z|  

我想将
created\u date
读入
DateType
,是否需要更改其他内容?

以下内容适用于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'")
}

您的示例显示了一个带时区的ISO时间戳(
Z
=Zero offset=
+00:00
=
UTC
=
GMT
),但您的输入格式没有提到处理时区的最后一个
X
@SamsonScharfrichter,我已经尝试过
yyyyy-MM-dd'T'hh:MM:ss
YYYY-MM-dd'T'hh:MM:ssZ
yyy-MM-dd'hh:MM:ssX
但结果仍然相同。我猜这与字节数组到日期的转换有关。我自己的猜测是,
ParquetRowConverter
类从未针对
dateFormat
选项进行过测试。。。因此,您必须读取数据帧中的文件,然后通过从第一个数据帧构造第二个数据帧来转换字段。这真的是个问题吗?幸运的是,“惰性评估”咒语意味着转换将在读取时完成,无论如何…@SamsonScharfrichter,这绝对好。我在我的代码中循环使用
dataframe
,只是想如果盒子里有我可以直接使用的东西,我会使用它。对不起,这会抛出
java.lang.AssertionError:assertion失败:时间戳(纳秒)应该存储在12字节长的二进制文件中,但得到了20字节的二进制文件。
。我想我需要创建一个新列(一旦我读取了数据框),并将字符串显式转换为日期,因为日期存储在拼花地板中的字符串中。
object DtFmt {
  val fmt = org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd'T‌​'HH:mm:ss'Z'")
}