我正在try 编写一个Python程序,该程序将接受任何XML文件作为输入,并将其转换为CSV文件,而不会丢失任何XML标记/元素.我对使用任何选项都持开放态度,只要它使用的是Python.

我try 使用了xmltodict、json、csv和pandas python模块,能够阅读XML并将其转换成词典.但我无法将此词典转换为可写入CSV文件的列表,以确保捕获所有的XML字段.

我的样例XML文件:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <tag_1>
    <tag_2>
      <date value="06-30-2023">
        <data>
          <tag_3>val_3</tag_3>
          <tag_4>val_4</tag_4>
          <tag_5>val_5_1 &amp; val_5_2</tag_5>
          <tag_6>-0.157</tag_6>
        </data>
        <data>
          <tag_3>val_3</tag_3>
          <tag_4>val_4_2</tag_4>
          <tag_5>val_5_1</tag_5>
          <tag_6>-0.173</tag_6>
        </data>
      </date>
    </tag_2>
    <tag_7>
      <date value="06-30-2023">
        <data><tag_3>val_3</tag_3><tag_4>val_4</tag_4><tag_5>val_5_1 &amp; val_5_2</tag_5><tag_6>-0.157</tag_6>
        </data>
        <data><tag_3>val_3</tag_3><tag_4>val_4_2</tag_4><tag_5>val_5_1</tag_5><tag_6>-0.173</tag_6>
        </data>
      </date>
    </tag_7>
  </tag_1>

在阅读了上面的XML之后,我能够将其转换为字典:

{'tag_1': 
  {'tag_2': 
    {'date': 
      {'@value': '06-30-2023', 
       'data': [{'tag_3': 'val_3', 'tag_4': 'val_4', 'tag_5': 'val_5_1 & val_5_2', 'tag_6': '-0.157'}, 
                {'tag_3': 'val_3', 'tag_4': 'val_4_2', 'tag_5': 'val_5_1', 'tag_6': '-0.173'}
           ]
      }
     }, 
   'tag_7': 
    {'date': 
      {'@value': '06-30-2023', 
       'data': [{'tag_3': 'val_3', 'tag_4': 'val_4', 'tag_5': 'val_5_1 & val_5_2', 'tag_6': '-0.157'}, 
                {'tag_3': 'val_3', 'tag_4': 'val_4_2', 'tag_5': 'val_5_1', 'tag_6': '-0.173'}
               ]
      }
    }
  }
}

我的预期输出(在CSV文件中)是:

tag_1,tag_2,date,data,tag_3,tag_4,tag_5,tag_6
tag_1,tag_2,06-30-2023,data,val_3,val_4,val_5_1 & val_5_2,-0.157
tag_1,tag_2,06-30-2023,data,val_3,val_4_2,val_5_1,-0.173
tag_1,tag_7,06-30-2023,data,val_3,val_4,val_5_1 & val_5_2,-0.157
tag_1,tag_7,06-30-2023,data,val_3,val_4_2,val_5_1,-0.173

到目前为止,我try 了以下几点:

import xmltodict
import json
import csv
import pandas as pd

with open("file_01.xml", "r", encoding="utf-8") as xml_fh:
    str_xml = xml_fh.read()

print(f"str_xml={type(str_xml)}={str_xml}")

dict_xml = xmltodict.parse(str_xml)
print(f"dict_xml={type(dict_xml)}={dict_xml}")
df = pd.DataFrame.from_dict(dict_xml, orient='index')
df.to_csv('file_01.csv', index = False)

我得到的实际结果是:

tag_2,tag_7
"{'date': {'@value': '06-30-2023', 'data': [{'tag_3': 'val_3', 'tag_4': 'val_4', 'tag_5': 'val_5_1 & val_5_2', 'tag_6': '-0.157'}, {'tag_3': 'val_3', 'tag_4': 'val_4_2', 'tag_5': 'val_5_1', 'tag_6': '-0.173'}]}}","{'date': {'@value': '06-30-2023', 'data': [{'tag_3': 'val_3', 'tag_4': 'val_4', 'tag_5': 'val_5_1 & val_5_2', 'tag_6': '-0.157'}, {'tag_3': 'val_3', 'tag_4': 'val_4_2', 'tag_5': 'val_5_1', 'tag_6': '-0.173'}]}}"

我错过了什么吗?

推荐答案

我们可以使用pd.json_normalize()来扁平化从XML创建的词典.然而,由于记录驻留在两个不同的键下:tag_2tag_7,我们需要遍历这些特定的标记以获得所有记录,然后连接数据帧.

import pandas as pd
import xmltodict

with open("file_01.xml", "r", encoding="utf-8") as xml_fh:
    str_xml = xml_fh.read()

dict_xml = xmltodict.parse(str_xml)

df = pd.concat(
    [
        pd.json_normalize(
            dict_xml, 
            record_path=['tag_1', tag, 'date', 'data'],            # path to record list
            meta=[['tag_1', tag, 'date', '@value']])               # path to date
        .pipe(lambda x: x.rename(columns={x.columns[-1]: 'date'})) # rename date column
        .assign(tag_1='tag_1', tag_2=tag, data='data')             # add meta columns
        for tag in ('tag_2', 'tag_7')                              # loop over tags
    ]
)[['tag_1', 'tag_2', 'date', 'data', 'tag_3', 'tag_4', 'tag_5', 'tag_6']]
df.to_csv('file_01.csv', index=False)

这将创建以下CSV文件:

tag_1,tag_2,date,data,tag_3,tag_4,tag_5,tag_6
tag_1,tag_2,06-30-2023,data,val_3,val_4,val_5_1 & val_5_2,-0.157
tag_1,tag_2,06-30-2023,data,val_3,val_4_2,val_5_1,-0.173
tag_1,tag_7,06-30-2023,data,val_3,val_4,val_5_1 & val_5_2,-0.157
tag_1,tag_7,06-30-2023,data,val_3,val_4_2,val_5_1,-0.173

也许更易维护的方法是标准化每个级别2关键字下的相关子词典.请注意,在下面的代码中,record_pathmeta路径不再是列表.

def flatten_dict(dict_xml, level_2_tags):
    df = (
        pd.concat([
            pd.json_normalize(dict_xml['tag_1'][tag]['date'], 'data', '@value')
            .assign(tag_2=tag)
            for tag in level_2_tags
        ])
        .rename(columns={'@value': 'date'})
        .assign(tag_1='tag_1', data='data')
        .get(['tag_1', 'tag_2', 'date', 'data', 'tag_3', 'tag_4', 'tag_5', 'tag_6'])
    )
    return df

# test run
flatten_dict(dict_xml, ['tag_2'])           # when there is only tag_2 in level=2

flatten_dict(dict_xml, ['tag_2', 'tag_7'])  # when there are 2 tags in level=2

Python-3.x相关问答推荐

根据其他数据框架的列顺序从数据框架中进行 Select

如何匹配字母,数字,短划线,逗号,但不是如果没有数字和字母?

使用Python请求从特定URL下载图像时出错

Pandas 插入的速度太慢了.对于跟踪代码,什么是更快的替代方案?

Strawberry FastAPI:如何调用正确的函数?

我正在try 从 10*3 矩阵中删除随机值并将其变为 10*2 矩阵

估计列表中连续对的数量

使用 python 查找标记的元素

如何计算Pandas 列中每列唯一项目的出现次数?

Python ** 用于负数

在python中循环处理时并行写入文件

ImportError:没有名为资源的模块

如何从脚本中提取 PDF 文档的标题以进行重命名?

Django 2 个字段之一不能为空

TypeError:JSON 对象必须是 str,而不是 'dict'

如何从另一个目录导入 python 包?

tkinter TclError:错误的文件类型使用 askopenfilename

Python 的 unittest 和 unittest2 模块有什么区别?

将列表列表转换为Python中的字典字典

在 linux mint 上安装 python3-venv 模块