在日常使用Spark处理数据时, 半结构化的JSON数据(JSONObject, JSONArray)可能是最常见的一种数据结构,那么能熟练并快速地处理JSON格式数据应该是数据开发人员的必备技能。
接下来我们就看看该如何将各种格式的JSON数据转成DataFrame。
1. 读取JSON文件
读取指定路径下的json文件(或者存放json文件的目录),加载为DataFrame。
本地文件
//本地路径
val path = "file:/opt/package/jar/bank_statement_detail.json"
spark.read.json(path).show()
HDFS文件
//HDFS路径
val path = "/file/bank_statement_detail.json"
spark.read.json(path).show()
2. List、Seq
将一个List或Seq加载为DataFrame。
import spark.implicits._
val ds = spark.createDataset("""[{"name": "Spark", "age": 10}, {"name": "Flink", "age": 5}]""" :: Nil)
spark.read
//默认的timestampFormat为 yyyy-MM-dd'T'HH:mm:ss.SSSXXX, SSS为毫秒数, XXX为非法字符(会报错"java.lang.IllegalArgumentException: Illegal pattern component: XXX"),
//需要手动设置时区为Z(即UTC时间)
.option("timestampFormat", "yyyy/MM/dd HH:mm:ss.SSSZ")
.json(ds)
.show()
3. JSON String类型的Column
JSON String类型的Column, 拿Hive来举例的话,就是Hive表中的某一列,其类型为String,其值是JSONArray类型("[{‘name’: ‘Spark’, ‘age’: 10}, {‘name’: ‘Flink’, ‘age’: 5}]"),或者是JSONObject类型("{‘name’: ‘Spark’, ‘age’: 10}")。
这种情况可以用以下两种方式处理:
方式一(map函数处理)
import com.alibaba.fastjson.JSON
import spark.implicits._
//person_info为string类型的JSONArray("[{'name': 'Spark', 'age': 10}, {'name': 'Flink', 'age': 5}]")
val df = spark.read.table("db_name.table_name").select("person_info")
//JSON string类型的Dataset转成DataFrame
val ds = df.map(row => JSON.parseArray(row.toString).get(0).toString)
ds.show()
方式二(指定schema)
import org.apache.spark.sql.types._
import spark.implicits._
//person_info为string类型的JSONArray("[{'name': 'Spark', 'age': 10}, {'name': 'Flink', 'age': 5}]")
val df = spark.read.table("db_name.table_name").select("person_info")
val schema = ArrayType(StructType(StructField("name", StringType):: StructField("age", IntegerType)::Nil))
val resultDF = df.select(from_json($"person_info", schema).as("info")) //将JSONArray列映射到一个schema上
.select(explode($"info").as("info")) //展开JSONArray中的元素(JSONObject)为多行
.select("info.name", "info.age") //获取每个JSONObject中每个key对应的value
resultDF.show()