0

我知道如何使用 xmltodict 将单个 xml 文件或链接转换为 python 中的 json。然而,我想知道是否有任何有效的方法可以在 Python 中将多个 xml 文件(数百甚至数千个)转换为 json?或者,如果有任何其他工具更适合它,而不是 Python?请注意,我不是一个非常熟练的程序员,只偶尔使用过 Python。

4

2 回答 2

0

这取决于您正在处理的具体案例。

我的示例案例(用于背景):

例如,有一次我必须从包含 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中实现了一个解决方案。我所做的是:

  1. 我已经使用iglob遍历这些目录,以便从每个目录中获取ann_morphosyntax.xml文件。
  2. 为了解析每个ann_morphosyntax.xml文件,我使用了ElementTree库。
  3. 我已将这些 JSON 保存在output.jsonl文件中。

解决方案

要自己尝试此解决方案,请执行以下操作:

  1. 运行此脚本以在项目根目录的输出目录中创建两个文件: example_1.xmlexample_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()

  1. 然后运行这个脚本,它将遍历example_1.xmlexample_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.xmlexample_2.xml文件中找到的 word 元素。

您可以详细说明该示例并使其更适合您的需求。

附言

第一个脚本基于 post Pretty printing XML in Python

于 2020-06-06T20:55:34.920 回答
0

我有同样的问题

我已经在 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 是我的分贝名称。

于 2020-11-11T09:09:35.403 回答