我知道如何使用 xmltodict 将单个 xml 文件或链接转换为 python 中的 json。然而,我想知道是否有任何有效的方法可以在 Python 中将多个 xml 文件(数百甚至数千个)转换为 json?或者,如果有任何其他工具更适合它,而不是 Python?请注意,我不是一个非常熟练的程序员,只偶尔使用过 Python。
2 回答
这取决于您正在处理的具体案例。
我的示例案例(用于背景):
例如,有一次我必须从包含 3890 个目录的大集合(100 万字的子语料库)(大约 2.6 GB)中读取数据,每个目录中都有一个ann_morphosyntax.xml文件。
来自ann_morphosyntax.xml文件之一的片段以供参考:
<?xml version="1.0" encoding="UTF-8"?>
<teiCorpus xmlns="http://www.tei-c.org/ns/1.0" xmlns:nkjp="http://www.nkjp.pl/ns/1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="NKJP_1M_header.xml"/>
<TEI>
<xi:include href="header.xml"/>
<text>
<body>
<p corresp="ann_segmentation.xml#segm_1-p" xml:id="morph_1-p">
<s corresp="ann_segmentation.xml#segm_1.5-s" xml:id="morph_1.5-s">
<seg corresp="ann_segmentation.xml#segm_1.1-seg" xml:id="morph_1.1-seg">
<fs type="morph">
<f name="orth">
<string>Jest</string>
</f>
这些ann_morphosyntax.xml文件中的每一个都包含一个或多个对象(为简单起见,我们说是段落),我需要将每个对象转换为 JSON 格式。这样的段落对象以<p
上面的 xml 文件片段开头。
此外,还需要将这些 JSON 保存在一个文件中,并将该文件的大小尽可能减小,因此我决定使用JSONL格式。这种文件格式允许您将每个 JSON 存储为该文件的一行,没有任何空格,这最终让我将初始数据集的大小减少到 450 MB 左右。
我已经在Python 3.6中实现了一个解决方案。我所做的是:
- 我已经使用iglob遍历这些目录,以便从每个目录中获取ann_morphosyntax.xml文件。
- 为了解析每个ann_morphosyntax.xml文件,我使用了ElementTree库。
- 我已将这些 JSON 保存在output.jsonl文件中。
解决方案:
要自己尝试此解决方案,请执行以下操作:
- 运行此脚本以在项目根目录的输出目录中创建两个文件: example_1.xml和example_2.xml:
import os
import xml.etree.ElementTree as ET
def prettify(element, indent=' '):
queue = [(0, element)] # (level, element)
while queue:
level, element = queue.pop(0)
children = [(level + 1, child) for child in list(element)]
if children:
element.text = '\n' + indent * (level+1) # for child open
if queue:
element.tail = '\n' + indent * queue[0][0] # for sibling open
else:
element.tail = '\n' + indent * (level-1) # for parent close
queue[0:0] = children # prepend so children come before siblings
def _create_word_object(sentence_object, number, word_string):
word = ET.SubElement(sentence_object, 'word', number=str(number))
string = ET.SubElement(word, 'string', number=str(number))
string.text = word_string
def create_two_xml_files():
xml_doc_1 = ET.Element('paragraph', number='1')
xml_doc_2 = ET.Element('paragraph', number='1')
sentence_1 = ET.SubElement(xml_doc_1, 'sentence', number='1')
sentence_2 = ET.SubElement(xml_doc_2, 'sentence', number='1')
_create_word_object(sentence_1, 1, 'This')
_create_word_object(sentence_2, 1, 'This')
_create_word_object(sentence_1, 2, 'is')
_create_word_object(sentence_2, 2, 'is')
_create_word_object(sentence_1, 3, 'first')
_create_word_object(sentence_2, 3, 'second')
_create_word_object(sentence_1, 4, 'example')
_create_word_object(sentence_2, 4, 'example')
_create_word_object(sentence_1, 5, 'sentence')
_create_word_object(sentence_2, 5, 'sentence')
_create_word_object(sentence_1, 6, '.')
_create_word_object(sentence_2, 6, '.')
prettify(xml_doc_1)
prettify(xml_doc_2)
tree_1 = ET.ElementTree(xml_doc_1)
tree_2 = ET.ElementTree(xml_doc_2)
os.mkdir('output')
tree_1.write('output/example_1.xml', encoding='UTF-8', xml_declaration=True)
tree_2.write('output/example_2.xml', encoding='UTF-8', xml_declaration=True)
def main():
create_two_xml_files()
if __name__ == '__main__':
main()
- 然后运行这个脚本,它将遍历example_1.xml和example_2.xml文件(使用 iglob)并使用第一步中创建的两个 XML 文件中的数据创建output.jsonl文件(将保存在项目的根目录中) :
import os
import glob
import errno
import jsonlines
import xml.etree.ElementTree as ET
class Word:
def __init__(self, word_id, word_text):
self.word_id = word_id
self.word_text = word_text
def create_word_dict(self):
return {"word": {"id": self.word_id, "text": self.word_text}}
def parse_xml(file_path):
for event, element in ET.iterparse(file_path, events=("start", "end",)):
if event == "end":
if element.tag == 'word':
yield Word(element[0].get('number'), element[0].text)
element.clear()
def write_dicts_from_xmls_in_directory_to_jsonlines_file(parsing_generator):
path = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) + '/output/*'
xml_files = glob.iglob(path)
with jsonlines.open('output.jsonl', mode='a') as writer:
for xml_file_name in xml_files:
try:
with open(xml_file_name):
for next_word in parsing_generator(xml_file_name):
writer.write(next_word.create_word_dict())
except IOError as exec:
if exec.errno != errno.EISDIR:
raise
def main():
write_dicts_from_xmls_in_directory_to_jsonlines_file(parse_xml)
if __name__ == '__main__':
main()
output.jsonl文件将在每一行中包含一个 JSON 对象,该对象表示可以在第一步生成的example_1.xml和example_2.xml文件中找到的 word 元素。
您可以详细说明该示例并使其更适合您的需求。
附言
第一个脚本基于 post Pretty printing XML in Python
我有同样的问题
我已经在 Python 中使用此代码来转换一个目录中的许多 xml 文件。
import xmltodict
import os
import json
path = "/home/bjorn/Nedlastinger/Doffin/1/5/"
for filename in os.listdir(path):
if not filename.endswith('.xml'):
continue
fullname = os.path.join(path, filename)
with open(fullname, 'r') as f:
xmlString = f.read()
jsonString = json.dumps(xmltodict.parse(xmlString, process_namespaces=True))
with open(fullname[:-4] + ".json", 'w') as f:
f.write(jsonString)
但它不控制数据类型。因此,每个数字都被转换为string
,之后您将获得一份清理数据的工作。
我已经在像 Couchbase 这样的 NoSQL 服务器中加载了所有内容。Couchbase 中用于将字符串转换为数字的代码是
UPDATE doffin SET DOFFIN_ESENDERS.FORM_SECTION.CONTRACT_AWARD.FD_CONTRACT_AWARD.AWARD_OF_CONTRACT.CONTRACT_VALUE_INFORMATION.COSTS_RANGE_AND_CURRENCY_WITH_VAT_RATE.VALUE_COST = TONUMBER(DOFFIN_ESENDERS.FORM_SECTION.CONTRACT_AWARD.FD_CONTRACT_AWARD.AWARD_OF_CONTRACT.CONTRACT_VALUE_INFORMATION.COSTS_RANGE_AND_CURRENCY_WITH_VAT_RATE.VALUE_COST);
doffin 是我的分贝名称。