Spark 解析XML文件到DataFrame

公司遇到一点需求,平时load文件基本上都是csv格式的文件,可是就有那么一个文件是xml文件,这也正常,因为文件是别的team推过来的,自然要遵循他们的格式,于是就要想办法解析xml文件。

目标是把xml文件转换为DataFrame,然后写到表中。

可是spark.reader并没有读取xml格式文件的方法,于是需要看有没有别的jar包辅助完成这项任务。

百度google搜索之后,确实发现有一个jar包可以解决

groupId: com.databricks
artifactId: spark-xml_2.11
version: 0.5.0

网上也有很多例子,但数官网将的比较清楚https://github.com/databricks/spark-xml

这上边的例子大家一看就会明白,也许能解决80%的问题,但是没能解决我的问题。

原因如下:

例子中的xml文件格式太简单,实际工作中的文件结构会很复杂,但是,例子中没有给出一个例子来处理复杂结构的xml文件。

继续找,找到一篇文件介绍了复杂文件结构如何解析,其实解析的方式到是一样的,只是选哪个节点作为root节点,以及怎样把嵌套的数组等拉平,此类API也许第一次没有接触过,不知道怎么使用。下面就来举一个例子。

<Item>
    <CDate>2018-05-08T00:00::00</CDate>
    <ListItemData>
        <ItemData>
            <IdKey>2</IdKeyData>
            <Value>1</Value>
        </ItemData>
        <ItemData>
            <IdKey>61</IdKeyData>
            <Value>2</Value>
        </ItemData>
    <ListItemData>
</Item>

这里这个例子就比官网的books.xml复杂,此时就不知道选谁作为rootTag,如果选择ItemData,那么无法获取CDate,如果选择Item,那么怎么把ItemData展开?

val innerSchema = StructType(
  StructField("ItemData",
    ArrayType(
      StructType(
        StructField("IdKey",LongType,true)::
          StructField("Value",LongType,true)::Nil
      )
    ),true)::Nil
)
val schema = StructType(
  StructField("CDate",StringType,true)::
  StructField("ListItemData", innerSchema, true):: Nil
)
import spark.implicits._
val df = spark.read.format("com.databricks.spark.xml")
  .option("rowTag", "Item")
  .schema(schema)
  .load(xmlFile)
  //Selecy nested field and explode to get the flattern result
  //把ItemData拉平
  .withColumn("ItemData", explode($"ListItemData.ItemData"))
  .select("CDate", "ItemData.*") // select required column

结果如下:

+--------------------+-----+-----+
|CDate               |IdKey|Value|
+--------------------+-----+-----+
|2018-05-08T00:00::00|2    |1    |
|2018-05-08T00:00::00|61   |2    |
+--------------------+-----+-----+

当然也可以省去schema,spark会根据xml文件推断schema

import spark.implicits._
val df = spark.read.format("com.databricks.spark.xml")
  .option("rowTag", "Item")
  //.schema(schema)
  .load(xmlFile)
  .withColumn("ItemData", explode($"ListItemData.ItemData"))
  .select("CDate", "ItemData.*")

注意:

问题1: 如果省去shcema会有什么问题?

由于spark会根据xml文件自动推断schema,如果xml文件局部节点不完整,不会有问题,如果全部文件都少掉了一个节点,那么推断出来的shcema将得不到你想要的完整的schema,例如:

<Item>
    <CDate>2018-05-08T00:00::00</CDate>
    <ListItemData>
        <ItemData>
            <IdKey>2</IdKeyData>
            <Value>1</Value>
        </ItemData>
        <ItemData>
            <IdKey>61</IdKeyData>
        </ItemData>
    <ListItemData>
</Item>

这个文件依然能推断出schema为:

root
 |-- CDate: string (nullable = true)
 |-- ListItemData: struct (nullable = true)
 |    |-- ItemData: array (nullable = true)
 |    |    |-- element: struct (containsNull = true)
 |    |    |    |-- IdKey: string (nullable = true)
 |    |    |    |-- Value: string (nullable = true)

但是下面的文件:

<Item>
    <CDate>2018-05-08T00:00::00</CDate>
    <ListItemData>
        <ItemData>
            <IdKey>2</IdKeyData>
        </ItemData>
        <ItemData>
            <IdKey>61</IdKeyData>
        </ItemData>
    <ListItemData>
</Item>

就不能推断出有Value节点,如果你要使用Value字段,将会报错,没有Value字段

root
 |-- CDate: string (nullable = true)
 |-- ListItemData: struct (nullable = true)
 |    |-- ItemData: array (nullable = true)
 |    |    |-- element: struct (containsNull = true)
 |    |    |    |-- IdKey: string (nullable = true)

如果你强制给他指定schema,那么就会为Value填充null值,但是不会报错Value字段不存在

强制指定schema后,schema为:

root
 |-- CDate: string (nullable = true)
 |-- ListItemData: struct (nullable = true)
 |    |-- ItemData: array (nullable = true)
 |    |    |-- element: struct (containsNull = true)
 |    |    |    |-- IdKey: string (nullable = true)
 |    |    |    |-- Value: string (nullable = true)

问题2:如何给字段重命名?

import spark.implicits._
val df = spark.read.format("com.databricks.spark.xml")
  .option("rowTag", "Item")
  //.schema(schema)
  .load(xmlFile)
  .withColumn("ItemData", explode($"ListItemData.ItemData"))
  .select("CDate", 
  "ItemData.IdKey as key",
  "ItemData.Value as value"
)

这样会报错,无法解析ItemData.IdKey 和ItemData.Value,使用如下方式即可:selectExpr

import spark.implicits._
val df = spark.read.format("com.databricks.spark.xml")
  .option("rowTag", "Item")
  //.schema(schema)
  .load(xmlFile)
  .withColumn("ItemData", explode($"ListItemData.ItemData"))
  .selectExpr("CDate", 
  "ItemData.IdKey as key",
  "ItemData.Value as value"
)

参考:

https://stackoverflow.com/questions/50237710/parsing-xml-files-with-scala

https://github.com/databricks/spark-xml

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值