DatabricksにおけるXMLのネイティブサポート

こちらのリリースです。

https://docs.databricks.com/en/release-notes/product/2024/january.html#native-xml-file-format-support-public-preview

**Native XML file format support (Public Preview)**

> XMLファイルフォーマットのネイティブサポートがパブリックプレビューとなりました。XMLファイルフォーマットのサポートによって、バッチ処理やストリーミングにおけるXMLデータの取り込み、クエリー、パースが買おうとなります。スキーマやデータタイプの自動推定や進化が可能となり、`from_xml`のようなSQLエクスプレッションをサポートし、XMLドキュメントを生成します。外部のjarは不要であり、Auto Loader、`read_files`、`COPY INTO`、Delta Live Tablesとシームレスに動作します。詳細については、[XMLファイルの読み取りと書き込み](https://docs.databricks.com/ja/query/formats/xml.html)をご覧ください。

:::note info
**プレビュー**
本機能は[パブリックプレビュー](https://docs.databricks.com/release-notes/release-types.html)です。
:::

これまでは、SparkでXMLを取り扱うためには、spark-xmlなどのライブラリが必要でした。ここではネイティブサポートをウォークスルーします。

# 要件

Databricksランタイム14.3以降が必要です。

# XMLレコードのパース

XMLはツリー構造なので、いきなりテーブル形式にできないケースが多いです。なので、データフレームの行に対応するタグを指定するためのオプションが指定されています。それが`rowTag`オプションです。このオプションでデータフレームの行に対応するタグを指定することができます。

最初にXMLファイルを作成します。以下の例では`books`タグの配下に`book`タグが並んでいるのがわかります。

```scala:scala
val xmlString = """
  <books>
    <book id="bk103">
      <author>Corets, Eva</author>
      <title>Maeve Ascendant</title>
    </book>
    <book id="bk104">
      <author>Corets, Eva</author>
      <title>Oberon's Legacy</title>
    </book>
  </books>"""
val xmlPath = "dbfs:/tmp/books.xml"
dbutils.fs.put(xmlPath, xmlString)
```

ですので、オプション`rowTag`に`books`を指定します。

```scala:scala
val df = spark.read.option("rowTag", "books").xml(xmlPath)
df.printSchema()
df.show(truncate=false)
```

動きました。

```
root
 |-- book: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- _id: string (nullable = true)
 |    |    |-- author: string (nullable = true)
 |    |    |-- title: string (nullable = true)

+------------------------------------------------------------------------------+
|book                                                                          |
+------------------------------------------------------------------------------+
|[{bk103, Corets, Eva, Maeve Ascendant}, {bk104, Corets, Eva, Oberon's Legacy}]|
+------------------------------------------------------------------------------+

df: org.apache.spark.sql.DataFrame = [book: array<struct<_id:string,author:string,title:string>>]
```

子要素の`book`を`rowTag`に指定します。

```scala:scala
val df = spark.read.option("rowTag", "book").xml(xmlPath)
display(df)
```

各要素がレコードになりました。
![Screenshot 2024-02-04 at 17.25.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/1a08f585-1405-cd6a-ae50-a431b90d281a.png)

# SQLのサンプル

```sql:SQL
DROP TABLE IF EXISTS books;
CREATE TABLE books
USING XML
OPTIONS (path "dbfs:/tmp/books.xml", rowTag "book");
SELECT * FROM books;
```
![Screenshot 2024-02-04 at 17.30.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/22788eb2-e7a6-7c70-37bf-21e6162e97f9.png)

# Pythonのサンプル

```py:Python
df = spark.read.format('xml').options(rowTag='book').load('dbfs:/tmp/books.xml')
(df.select("author", "_id").write
  .options(rowTag='book', rootTag='books')
  .xml('dbfs:/tmp/newbooks.xml')
)
```

```py:Python
new_df = spark.read.format('xml').options(rowTag='book').load('dbfs:/tmp/newbooks.xml')
display(new_df)
```
![Screenshot 2024-02-04 at 17.32.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/d54276a0-59f2-2d86-afd1-edcf34cf3072.png)


### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)