PySpark解析带命名空间XML并扁平化生成结构化表方法
解决方案
之前代码的核心问题有两个:一是XML读取时rowTag定位的层级不对,二是炸开属性数组后没有做键值映射和行转列,自然无法得到结构化宽表。按下面步骤修改即可直接得到目标结果:
1. 调整XML读取参数
开启忽略命名空间配置,省掉所有ns1:前缀的转义麻烦,同时修正根标签和行标签配置:
from pyspark.sql import SparkSession from pyspark.sql.functions import explode, first, col df = spark.read \ .format("com.databricks.spark.xml") \ .option("rootTag", "report") \ .option("rowTag", "entities") \ .option("ignoreNamespace", "true") \ .load("/data/test/abc.xml")
注:如果你的spark-xml版本不识别ignoreNamespace参数,替换为namespace="false"即可,效果都是自动剥离标签上的ns1前缀,后续直接用entity/attribute访问字段,不需要额外转义。
2. 炸开嵌套层级提取属性键值对
读取到的第一层是entities节点,下面嵌套了两层entity,逐层炸开后提取每个属性的name(对应目标表字段名)和标签文本(对应字段内容):
# 炸开第一层PHYSICIANS类型的实体,拿到内层医生实体数组 physician_group_df = df.select(explode("entity").alias("physician_collect")) # 炸开第二层拿到每个独立医生的记录 physician_df = physician_group_df.select( explode("physician_collect.entity").alias("physician") ) # 炸开每个医生下的属性数组,提取字段名、字段值和医生唯一标识 attr_long_df = physician_df.select( col("physician._instance").alias("physician_id"), explode("physician.attribute").alias("attr") ).select( "physician_id", col("attr._name").alias("field_name"), col("attr._VALUE").alias("field_value") )
3. 透视转换为目标宽表
用pivot把长表格式的键值对转成列,空字段自动填充null,和预期表结构完全匹配:
result_df = attr_long_df.groupBy("physician_id") \ .pivot("field_name") \ .agg(first("field_value", ignorenulls=True)) \ .drop("physician_id") # 输出结果验证 result_df.show(truncate=False)
执行后输出结果如下,完全匹配示例结构:
| ID | NAME | ADDRESS_LINE_1 | ADDRESS_LINE_2 | COUNTRY | STATE | CITY |
|---|---|---|---|---|---|---|
| 207 | Dr. George | 903 Lake Otis pkway | null | United States of America | Alaska | Anchorage |
| 208 | Dr. James Hanover | 220 Plano pkway | null | United States of America | Texas | Dallas |
补充说明
- 如果后续需要保留属性上的tc、code这类附加字段(比如COUNTRY对应的code值
OLI_NATION_USA),在提取attr字段时直接取attr._code、attr._tc字段即可。 - 如果遇到同名属性对应多值的场景,把聚合逻辑里的
first换成collect_list就能完整保留所有值。
内容的提问来源于stack exchange,提问作者shaz_nwaz
相关产品推荐
相关产品推荐

