簡體   English   中英

如何在 Palantir Foundry 中解析 xml 文檔?

[英]How do I parse xml documents in Palantir Foundry?

我有一組要解析的.xml文檔。

我以前曾嘗試使用獲取文件內容並將它們轉儲到單個單元格中的方法來解析它們,但是我注意到這在實踐中不起作用,因為我看到運行時間越來越慢,通常需要完成一項任務運行數十小時:

我的第一個轉換采用.xml內容並將其放入單個單元格,第二個轉換采用此字符串並使用 Python 的xml庫將字符串解析為文檔。 然后我可以從該文檔中提取屬性並返回 DataFrame。

我正在使用UDF來執行將字符串內容映射到我想要的字段的過程。

如何使用大型.xml文件使其更快/更好地工作?

對於這個問題,我們將結合幾種不同的技術來使這段代碼既可測試又具有高度可擴展性。

理論

解析原始文件時,您可以考慮以下幾個選項:

  1. ❌ 您可以編寫自己的解析器來從文件中讀取字節並將它們轉換為 Spark 可以理解的數據。
    • 由於工程時間和不可擴展的架構,盡可能不鼓勵這樣做。 當您這樣做時,它不會利用分布式計算,因為您必須將整個原始文件帶到您的解析方法中,然后才能使用它。 這不是對資源的有效利用。
  2. ⚠ 您可以使用自己的非 Spark 解析器庫,例如問題中提到的 XML Python 庫
    • 雖然這比編寫自己的解析器更容易實現,但它仍然沒有利用 Spark 中的分布式計算。 運行起來更容易,但最終會遇到性能限制,因為它沒有利用僅在編寫 Spark 庫時才公開的低級 Spark 功能。
  3. ✅ 你可以使用 Spark 原生的原始文件解析器
    • 這是所有情況下的首選選項,因為它利用了低級 Spark 功能,並且不需要您編寫自己的代碼。 如果存在低級 Spark 解析器,則應使用它。

在我們的例子中,我們可以使用 Databricks 解析器來獲得很好的效果。

通常,您還應該避免使用.udf方法,因為它可能正在使用,而不是 Spark API 中已有的良好功能。 UDF 的性能不如本機方法,只有在沒有其他選項可用時才應使用。

UDF 掩蓋隱藏問題的一個很好的例子是對列內容的字符串操作; 雖然從技術上講,您可以使用 UDF 來執行拆分和修剪字符串等操作,但這些內容已經存在於Spark API中,並且比您自己的代碼快幾個數量級。

設計

我們的設計將使用以下內容:

  1. 通過Databricks XML 解析器完成的低級 Spark 優化文件解析
  2. 測試驅動的原始文件解析,如此所述

連接解析器

首先,我們需要將.jar添加到 Transforms 中可用的spark_session 由於最近的改進,此參數在配置后將允許您在預覽/測試和完整構建時使用.jar 以前,這需要一個完整的構建,但現在不需要。

我們需要 go 到我們的transforms-python/build.gradle文件並添加 2 個配置塊:

  1. 啟用pytest插件
  2. 啟用condaJars參數並聲明.jar依賴項

我的/transforms-python/build.gradle現在如下所示:

buildscript {
    repositories {
       // some other things
    }

    dependencies {
        classpath "com.palantir.transforms.python:lang-python-gradle-plugin:${transformsLangPythonPluginVersion}"
    }
}

apply plugin: 'com.palantir.transforms.lang.python'
apply plugin: 'com.palantir.transforms.lang.python-defaults'

dependencies {
    condaJars "com.databricks:spark-xml_2.13:0.14.0"
}

// Apply the testing plugin
apply plugin: 'com.palantir.transforms.lang.pytest-defaults'

// ... some other awesome features you should enable

應用此配置后,您需要通過單擊底部功能區並點擊Refresh來重新啟動 Code Assist session

刷新

刷新 Code Assist 后,我們現在可以使用低級功能來解析我們的.xml文件,現在我們需要對其進行測試!

測試解析器

如果我們采用與這里相同的測試驅動開發風格,我們最終會得到/transforms-python/src/myproject/datasets/xml_parse_transform.py ,其內容如下:

from transforms.api import transform, Output, Input
from transforms.verbs.dataframes import union_many


def read_files(spark_session, paths):
    parsed_dfs = []
    for file_name in paths:
        parsed_df = spark_session.read.format('xml').options(rowTag="tag").load(file_name)
        parsed_dfs += [parsed_df]
    output_df = union_many(*parsed_dfs, how="wide")
    return output_df


@transform(
    the_output=Output("my.awesome.output"),
    the_input=Input("my.awesome.input"),
)
def my_compute_function(the_input, the_output, ctx):
    session = ctx.spark_session
    input_filesystem = the_input.filesystem()
    hadoop_path = input_filesystem.hadoop_path
    files = [hadoop_path + file_name for file_name in input_filesytem.ls("**/*.xml")]
    output_df = read_files(session, files)
    the_output.write_dataframe(output_df)

...一個示例文件/transforms-python/test/myproject/datasets/sample.xml內容:

<tag>
<field1>
my_value
</field1>
</tag>

還有一個測試文件/transforms-python/test/myproject/datasets/test_xml_parse_transform.py

from myproject.datasets import xml_parse_transform
from pkg_resources import resource_filename


def test_parse_xml(spark_session):
    file_path = resource_filename(__name__, "sample.xml")
    parsed_df = xml_parse_transform.read_files(spark_session, [file_path])
    assert parsed_df.count() == 1
    assert set(parsed_df.columns) == {"field1"}

我們現在有:

  1. 一種分布式計算的低級.xml解析器,具有高度可擴展性
  2. 一個測試驅動的設置,我們可以快速迭代以使我們的確切功能正確

干杯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM