Azure Databricks Spark(Scala)读取多行JSON带Schema返回全Null行问题
你遇到的问题核心是Schema中time字段的类型与JSON中的数据格式不匹配,结合multiLine选项的特性,导致Spark解析失败,最终返回全Null的记录。
具体原因
JSON里的time字段是纯时间字符串(如"17:05:23"),但你在Schema中定义为TimestampType。Spark默认的Timestamp解析规则要求字符串包含日期+时间的完整格式(比如yyyy-MM-dd HH:mm:ss),纯时间字符串无法被解析为Timestamp;而Spark默认开启容错模式(mode="PERMISSIVE"),会把解析失败的整条记录置为Null。
至于不指定Schema时能正常读取,是因为Spark自动推断类型时会把time识别为StringType,避开了解析失败的问题。
解决方案
方案1:修改Schema中time字段为StringType
直接将time字段类型改为字符串,后续可根据需求再转换为时间类型:
val pitStopsSchema = StructType(Array( StructField("driverId", IntegerType, true), StructField("duration", DoubleType, true), StructField("lap", IntegerType, true), StructField("milliseconds", LongType, true), StructField("raceId", IntegerType, true), StructField("stop", IntegerType, true), StructField("time", StringType, true) // 改为StringType )) var df = spark.read.schema(pitStopsSchema) .option("multiLine", true) .json("dbfs:/mnt/kchirchir/formulaone/raw/pit_stops.json")
方案2:指定Timestamp解析格式适配纯时间字符串
如果需要保留TimestampType,可以通过timestampFormat选项指定时间字符串格式,让Spark正确解析:
val pitStopsSchema = StructType(Array( StructField("driverId", IntegerType, true), StructField("duration", DoubleType, true), StructField("lap", IntegerType, true), StructField("milliseconds", LongType, true), StructField("raceId", IntegerType, true), StructField("stop", IntegerType, true), StructField("time", TimestampType, true) )) var df = spark.read.schema(pitStopsSchema) .option("multiLine", true) .option("timestampFormat", "HH:mm:ss") // 指定纯时间格式 .json("dbfs:/mnt/kchirchir/formulaone/raw/pit_stops.json")
方案3:改用TimeType类型(Spark 3.0+支持)
如果只需要存储时间信息、不需要日期,可以使用TimeType替代TimestampType,它专门处理HH:mm:ss格式的纯时间字符串:
import org.apache.spark.sql.types.TimeType val pitStopsSchema = StructType(Array( StructField("driverId", IntegerType, true), StructField("duration", DoubleType, true), StructField("lap", IntegerType, true), StructField("milliseconds", LongType, true), StructField("raceId", IntegerType, true), StructField("stop", IntegerType, true), StructField("time", TimeType, true) // 使用TimeType )) var df = spark.read.schema(pitStopsSchema) .option("multiLine", true) .json("dbfs:/mnt/kchirchir/formulaone/raw/pit_stops.json")
额外检查点
确认你的JSON文件整体结构是否为数组包裹(比如[ {...}, {...} ])。如果文件只是多个独立JSON对象用逗号分隔而没有外层数组,虽然multiLine=true能让Spark读取,但结合Schema解析时可能出现隐性不匹配。不过从你不指定Schema能正常读取的情况来看,这个问题影响较小,核心还是time字段的类型匹配问题。
内容的提问来源于stack exchange,提问作者Kenneth Chirchir

