Spark 解析XML檔案到DataFrame

2021-09-13 18:58:03 字數 4422 閱讀 6529

公司遇到一點需求,平時load檔案基本上都是csv格式的檔案,可是就有那麼乙個檔案是xml檔案,這也正常,因為檔案是別的team推過來的,自然要遵循他們的格式,於是就要想辦法解析xml檔案。

目標是把xml檔案轉換為dataframe,然後寫到表中。

可是spark.reader並沒有讀取xml格式檔案的方法,於是需要看有沒有別的jar包輔助完成這項任務。

groupid: com.databricks

artifactid: spark-xml_2.11

version: 0.5.0

網上也有很多例子,但數官網將的比較清楚

這上邊的例子大家一看就會明白,也許能解決80%的問題,但是沒能解決我的問題。

原因如下:

例子中的xml檔案格式太簡單,實際工作中的檔案結構會很複雜,但是,例子中沒有給出乙個例子來處理複雜結構的xml檔案。

繼續找,找到一篇檔案介紹了複雜檔案結構如何解析,其實解析的方式到是一樣的,只是選哪個節點作為root節點,以及怎樣把巢狀的陣列等拉平,此類api也許第一次沒有接觸過,不知道怎麼使用。下面就來舉乙個例子。

2018-05-08t00:00::0021

612

這裡這個例子就比官網的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,例如:

2018-05-08t00:00::0021

61

這個檔案依然能推斷出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)

但是下面的檔案:

2018-05-08t00:00::00

2 61

就不能推斷出有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"

)

參考:

建立xml檔案 解析xml檔案

import codecs import xml.dom.minidom doc xml.dom.minidom.document print doc root doc.createelement booklist print u 新增的xml標籤為 root.tagname root.setatt...

解析XML檔案

sax解析xml 得到saxparse ctory saxparse ctory saxparse ctory saxparse ctory.newinstance 得到saxparser saxparser saxparser saxparse ctory.newsaxparser 得到xmlre...

XML檔案解析

xml是可擴充套件標記語言,用來傳輸和儲存資料。xml文件必須包含根元素,該元素是所有其他元素的父元素。xml文件中的元素形成了樹形結構。xml有以下特點 建立名稱是 textfile1.txt 的文件,設定屬性 複製到輸出目錄 如果較新則複製 在工程執行時,會自動將該txt檔案複製到bin下面。2...