公司遇到一點需求,平時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...