JSON 中正确的 Date/Datetime 格式是什么,Spark SQL 可以自动为其推断模式?
What is the right Date/Datetime format in JSON for Spark SQL to automatically infer the schema for it?
Spark SQL 支持从 JSON 输入源自动推断模式(每一行都是一个独立的 JSON 文件)——它通过扫描整个数据集来实现创建架构,但它仍然有用。 (我说的是 1.2.1,不是新的 1.3,所以可能会有一些变化)
我看到一些关于支持/不支持它的相互矛盾的帖子,但我认为它是最近添加的(在 1.2 中)
我的问题是 - 在 JSON 中为 Spark SQL 格式化 Date/Datetime/Timestamp 以在其自动模式推理机制中识别它的正确方法是什么?
JSON 类型推断永远不会推断日期类型。非零长度字符串总是被推断为字符串。 Source code:
private[sql] object InferSchema {
// ...
private def inferField(parser: JsonParser): DataType = {
import com.fasterxml.jackson.core.JsonToken._
parser.getCurrentToken match {
// ...
case VALUE_STRING => StringType
// ...
}
}
// ...
}
对于自动检测,必须将其更改为查看实际字符串 (parser.getValueAsString
) 并在适当时基于格式 return DateType
。
第二步采用正常的自动生成模式并转换日期类型可能更简单。
另一种选择是读取一小部分数据样本(不使用 Spark)并自行推断模式。然后使用您的模式创建 DataFrame。这也避免了一些计算。
可以使用您选择的格式(我使用的是 Date.toJSON 格式)来推断日期,稍作修改并且性能也不错。
获取最新的维护分支:
git clone https://github.com/apache/spark.git
cd spark
git checkout branch-1.4
替换InferSchema中的以下块:
case VALUE_STRING if parser.getTextLength < 1 =>
// Zero length strings and nulls have special handling to deal
// with JSON generators that do not distinguish between the two.
// To accurately infer types for empty strings that are really
// meant to represent nulls we assume that the two are isomorphic
// but will defer treating null fields as strings until all the
// record fields' types have been combined.
NullType
case VALUE_STRING => StringType
使用以下代码:
case VALUE_STRING =>
val len = parser.getTextLength
if (len < 1) {
NullType
} else if (len == 24) {
// try to match dates of the form "1968-01-01T12:34:56.789Z"
// for performance, only try parsing if text is 24 chars long and ends with a Z
val chars = parser.getTextCharacters
val offset = parser.getTextOffset
if (chars(offset + len - 1) == 'Z') {
try {
org.apache.spark.sql.catalyst.util.
DateUtils.stringToTime(new String(chars, offset, len))
TimestampType
} catch {
case e: Exception => StringType
}
} else {
StringType
}
} else {
StringType
}
Build Spark 根据您的设置。我用过:
mvn -Pyarn -Phadoop-2.6 -Dhadoop.version=2.6.0 -DskipTests=true clean install
要进行测试,请在顶层创建一个名为 datedPeople.json
的文件,其中包含以下数据:
{"name":"Andy", "birthdate": "2012-04-23T18:25:43.511Z"}
{"name":"Bob"}
{"name":"This has 24 characters!!", "birthdate": "1988-11-24T11:21:13.121Z"}
{"name":"Dolla Dolla BillZZZZZZZZ", "birthdate": "1968-01-01T12:34:56.789Z"}
读入文件。确保在完全使用 sqlContext
之前设置 conf 选项,否则它不会工作。日期!
.\bin\spark-shell.cmd
scala> sqlContext.setConf("spark.sql.json.useJacksonStreamingAPI", "true")
scala> val datedPeople = sqlContext.read.json("datedPeople.json")
datedPeople: org.apache.spark.sql.DataFrame = [birthdate: timestamp, name: string]
scala> datedPeople.foreach(println)
[2012-04-23 13:25:43.511,Andy]
[1968-01-01 06:34:56.789,Dolla Dolla BillZZZZZZZZ]
[null,Bob]
[1988-11-24 05:21:13.121,This has 24 characters!!]
自 Spark 3.0 起,JSON 数据源从字符串值推断 TimestampType
如果它们匹配 JSON 选项定义的模式 timestampFormat
。
选项 inferTimestamp
可以设置为 false
以禁用此类类型推断。
在这里提供 2021 年的更新,此时字符串可以推断为 TimestampType,但不能推断为 DateType。看:
https://spark.apache.org/docs/latest/api/java/org/apache/spark/sql/DataFrameReader.html#json-scala.collection.Seq-
您可以在 spark.read.format("json")
或 from_json()
中设置一个选项来处理被解析为时间戳的格式。该选项是 timestampFormat
并在此处进行了描述:https://spark.apache.org/docs/latest/api/java/org/apache/spark/sql/DataFrameReader.html#json-scala.collection.Seq-
Spark SQL 支持从 JSON 输入源自动推断模式(每一行都是一个独立的 JSON 文件)——它通过扫描整个数据集来实现创建架构,但它仍然有用。 (我说的是 1.2.1,不是新的 1.3,所以可能会有一些变化)
我看到一些关于支持/不支持它的相互矛盾的帖子,但我认为它是最近添加的(在 1.2 中)
我的问题是 - 在 JSON 中为 Spark SQL 格式化 Date/Datetime/Timestamp 以在其自动模式推理机制中识别它的正确方法是什么?
JSON 类型推断永远不会推断日期类型。非零长度字符串总是被推断为字符串。 Source code:
private[sql] object InferSchema {
// ...
private def inferField(parser: JsonParser): DataType = {
import com.fasterxml.jackson.core.JsonToken._
parser.getCurrentToken match {
// ...
case VALUE_STRING => StringType
// ...
}
}
// ...
}
对于自动检测,必须将其更改为查看实际字符串 (parser.getValueAsString
) 并在适当时基于格式 return DateType
。
第二步采用正常的自动生成模式并转换日期类型可能更简单。
另一种选择是读取一小部分数据样本(不使用 Spark)并自行推断模式。然后使用您的模式创建 DataFrame。这也避免了一些计算。
可以使用您选择的格式(我使用的是 Date.toJSON 格式)来推断日期,稍作修改并且性能也不错。
获取最新的维护分支:
git clone https://github.com/apache/spark.git
cd spark
git checkout branch-1.4
替换InferSchema中的以下块:
case VALUE_STRING if parser.getTextLength < 1 =>
// Zero length strings and nulls have special handling to deal
// with JSON generators that do not distinguish between the two.
// To accurately infer types for empty strings that are really
// meant to represent nulls we assume that the two are isomorphic
// but will defer treating null fields as strings until all the
// record fields' types have been combined.
NullType
case VALUE_STRING => StringType
使用以下代码:
case VALUE_STRING =>
val len = parser.getTextLength
if (len < 1) {
NullType
} else if (len == 24) {
// try to match dates of the form "1968-01-01T12:34:56.789Z"
// for performance, only try parsing if text is 24 chars long and ends with a Z
val chars = parser.getTextCharacters
val offset = parser.getTextOffset
if (chars(offset + len - 1) == 'Z') {
try {
org.apache.spark.sql.catalyst.util.
DateUtils.stringToTime(new String(chars, offset, len))
TimestampType
} catch {
case e: Exception => StringType
}
} else {
StringType
}
} else {
StringType
}
Build Spark 根据您的设置。我用过:
mvn -Pyarn -Phadoop-2.6 -Dhadoop.version=2.6.0 -DskipTests=true clean install
要进行测试,请在顶层创建一个名为 datedPeople.json
的文件,其中包含以下数据:
{"name":"Andy", "birthdate": "2012-04-23T18:25:43.511Z"}
{"name":"Bob"}
{"name":"This has 24 characters!!", "birthdate": "1988-11-24T11:21:13.121Z"}
{"name":"Dolla Dolla BillZZZZZZZZ", "birthdate": "1968-01-01T12:34:56.789Z"}
读入文件。确保在完全使用 sqlContext
之前设置 conf 选项,否则它不会工作。日期!
.\bin\spark-shell.cmd
scala> sqlContext.setConf("spark.sql.json.useJacksonStreamingAPI", "true")
scala> val datedPeople = sqlContext.read.json("datedPeople.json")
datedPeople: org.apache.spark.sql.DataFrame = [birthdate: timestamp, name: string]
scala> datedPeople.foreach(println)
[2012-04-23 13:25:43.511,Andy]
[1968-01-01 06:34:56.789,Dolla Dolla BillZZZZZZZZ]
[null,Bob]
[1988-11-24 05:21:13.121,This has 24 characters!!]
自 Spark 3.0 起,JSON 数据源从字符串值推断 TimestampType
如果它们匹配 JSON 选项定义的模式 timestampFormat
。
选项 inferTimestamp
可以设置为 false
以禁用此类类型推断。
在这里提供 2021 年的更新,此时字符串可以推断为 TimestampType,但不能推断为 DateType。看: https://spark.apache.org/docs/latest/api/java/org/apache/spark/sql/DataFrameReader.html#json-scala.collection.Seq-
您可以在 spark.read.format("json")
或 from_json()
中设置一个选项来处理被解析为时间戳的格式。该选项是 timestampFormat
并在此处进行了描述:https://spark.apache.org/docs/latest/api/java/org/apache/spark/sql/DataFrameReader.html#json-scala.collection.Seq-