объединение файлов xml с использованием дерева элементов в Python

Я пытаюсь объединить два файла xml. Файлы имеют одинаковую общую структуру, но отличаются деталями.

файл1.xml:

<book>
    <chapter id="113">
        <sentence id="1">
            <word id="128160">
                <POS Tag="V"/>
                <grammar type="STEM"/>
                <Aspect type="IMPV"/>
                <Number type="S"/>
            </word>
            <word id="128161">
                <POS Tag="V"/>
                <grammar type="STEM"/>
                <Aspect type="IMPF"/>
            </word>
             </sentence>
             <sentence id="2">
            <word id="128162">
                <POS Tag="P"/>
                <grammar type="PREFIX"/>
                <Tag Tag="bi+"/>
            </word>
             </sentence>
        </chapter>
</book>

файл2.xml:

<book>
    <chapter id="113">
        <sentence id="1">
            <word id="128160">
            <concept English="joke"/>
            </word>
            <word id="128161">
                <concept English="romance"/>
            </word>
             </sentence>
             <sentence id="2">
            <word id="128162">
                <concept English="happiness"/>
            </word>
             </sentence>
        </chapter>
</book>

Желаемый результат:

<book>
    <chapter id="113">
        <sentence id="1">
            <word id="128160">
                    <concept English="joke"/>
                    <POS Tag="V"/>
                <grammar type="STEM"/>
                <Aspect type="IMPV"/>
                <Number type="S"/>
            </word>
            <word id="128161">
                <concept English="romance"/>
                <POS Tag="V"/>
                <grammar type="STEM"/>
                <Aspect type="IMPF"/>
            </word>
             </sentence>
             <sentence id="2">
            <word id="128162">
                <concept English="happiness"/>
                <POS Tag="P"/>
                <grammar type="PREFIX"/>
                <Tag Tag="bi+"/>
            </word>
             </sentence>
        </chapter>
</book>

Хорошо, я попытался сделать это в пути, но не получил желаемого результата:

import os, os.path, sys
import glob
from xml.etree import ElementTree

output = open('merge.xml','w')
files="sample"
xml_files = glob.glob(files +"/*.xml")
xml_element_tree = None
for xml_file in xml_files:
        data = ElementTree.parse(xml_file).getroot()
        # print ElementTree.tostring(data)
        for word in data.iter('word'):
            if xml_element_tree is None:
                xml_element_tree = data 
                insertion_point = xml_element_tree.findall("book/chapter/sentence/word/*")
            else:
                insertion_point.extend(word) 
if xml_element_tree is not None:
        print>>output, ElementTree.tostring(xml_element_tree)

пожалуйста, любая помощь


person spring rose    schedule 25.07.2013    source источник


Ответы (3)


В прошлом я делал что-то подобное, чтобы создать документ xml, а затем добавить значения, которые вы ищете. Я не верю, что есть способ их "объединить"

xml = ET.fromstring("<book></book>")
document = ET.parse(tempFile)
childNodeList = document.findall(xpathQuery)
for node in childNodeList: 
   xml.append(node)
person Brad    schedule 25.07.2013
comment
Хорошо, но как получить правильный запрос xpath в моих файлах? как сравнить, если два файла содержат один и тот же идентификатор слова, а затем скопировать и создать новый файл xml? - person spring rose; 26.07.2013
comment
Что ж. это отдельные вопросы. вы спросили, как объединить два файла xml. для вашего запроса xpath я бы посмотрел здесь: docs .python.org/2/library/ Что касается сравнения идентификатора слова, вам нужно будет выполнить запрос xpath, чтобы получить список совпадающих узлов, выполнить итерацию по нему и сравнить идентификатор слова и его не в вашем новом xml, затем добавьте его. Эта часть действительно вопрос алгоритма... - person Brad; 26.07.2013

Вот решение. Начните с пустого объединенного документа, а затем, перечисляя файлы, добавляйте в объединенный документ элементы, которые вы не можете найти. Вы можете обобщить это, но вот первый разрез:

import lxml.etree
merged = lxml.etree.Element('book')
for xml_file in xml_files:
    for merge_chapter in lxml.etree.parse(xml_file):
        try:
            chapter = merged.xpath('chapter[@id=%s]' % merge_chapter.get('id'))[0]
            for merge_sentence in merge_chapter:
                try:
                    sentence = chapter.xpath('sentence[@id=%s]' % merge_sentence.get('id'))[0]
                    for merge_word in merge_sentence:
                        try:
                            word = sentence.xpath('word[@id=%s]' % merge_word.get('id'))[0]
                            for data in merge_word:
                                try:
                                    word.xpath(data.tag)[0]
                                except IndexError:
                                    # add newly discovered word data
                                    word.append(data)
                        except IndexError:
                            # add newly discovered word
                            sentence.append(merge_word)
                except IndexError:
                    # add newly discovered sentence
                    chapter.append(merge_sentence)
        except IndexError:
            # add newly discovered chapter
            merged.append(merge_chapter)
person tdelaney    schedule 25.07.2013
comment
привет, спасибо за вашу помощь, я попытался запустить код, но я пришел с этой ошибкой: AttributeError: объект «ElementTree» не имеет атрибута «элемент» - person spring rose; 26.07.2013
comment
Я работаю в модели дерева элементов xml. в какой модели работает код? - person spring rose; 26.07.2013
comment
ой... мой плохой. Я переключил туда lxml и ElementTree. lxml имеет отличный парсер xpath, и я предпочитаю его ElementTree. Я сделал правку. - person tdelaney; 26.07.2013
comment
хорошо ли использовать исключения в качестве операций управления потоком? - person LB40; 12.01.2015

Учитывая, что вы хотите объединить File2 в File1, вы можете перебрать все элементы в File2, а затем скопировать атрибуты из элемента File2 в элемент File1.

Мне нужно сделать что-то подобное в проекте, над которым я сейчас работаю. Вот мое текущее решение, которое должно работать под Python 2.7.

Обратите внимание, что я дополнительно добавил к требованиям копирование атрибутов между общими узлами. Вы увидите, что я добавил к A следующие атрибуты:

  • барабаны = 'Нил'
  • бас = 'Гедди'

Затем к Б я добавил:

  • гитара='Алекс'

В окончательном объединенном документе есть все три члена властного трио.

Я также добавил <sentance id='3'/>, чтобы продемонстрировать, что порядок элементов больше не имеет значения.

#!/usr/bin/python
from lxml import etree 
from copy import deepcopy
import lxml

xmlA='''
<book>
    <chapter id="113">

        <sentence id="1" drums='Neil'>
            <word id="128160" bass='Geddy'>
                <POS Tag="V"/>
                <grammar type="STEM"/>
                <Aspect type="IMPV"/>
                <Number type="S"/>
            </word>
            <word id="128161">
                <POS Tag="V"/>
                <grammar type="STEM"/>
                <Aspect type="IMPF"/>
            </word>
        </sentence>

        <sentence id="2">
            <word id="128162">
                <POS Tag="P"/>
                <grammar type="PREFIX"/>
                <Tag Tag="bi+"/>
            </word>
        </sentence>

    </chapter>
</book>
'''

xmlB='''
<book>
    <chapter id="113">

        <sentence id="3">
            <word id="128168">
                <concept English="sadness"/>
            </word>
        </sentence>

        <sentence id="1">
            <word id="128160">
                <concept English="joke"/>
            </word>
            <word id="128161">
                <concept English="romance"/>
            </word>
        </sentence>

        <sentence id="2" guitar='Alex'>
            <word id="128162">
                <concept English="happiness"/>
            </word>
        </sentence>


    </chapter>
</book>
'''

import re
from copy import deepcopy

##
#   @brief  Translates the relational xpath to an explicit xpath.
#   In the XML examples above, getpath will return the following for 
#   <sentance id='1'/>:
#       - xmlA = /book/chapter/sentance[1]
#       - xmlb = /book/chapter/sentance[2]
#
#   A path that is explicit in both document would be:
#       - xmlA = /book/chapter/sentance[@id='1']
#       - xmlb = /book/chapter/sentance[@id='1']
#
def convertXpath(element):
    newPath = ''
    tree    = element.getroottree()
    path    = tree.getpath(element).split('/')
    root    = tree.getroot()

    for p in path:
        if p == '':
            continue

        if re.search('\[[0-9]*\]', p):

            # Get the element at this path
            #
            node = root.xpath(newPath+'/'+p)[0]
            id=node.get('id')

            p=re.sub('\[[0-9]*\]','', p)
            newPath += '/'+p+"[@id='"+id+"']"

        else:
            newPath+='/'+p

    return newPath



def mergeXml(a,b):

    for node in a.nodes():
        path = convertXpath(node)

        # find the element in the other document
        #
        elements =  b.root.xpath(path)

        for e in elements:
            for name, value in node.items():
                if name == 'id':
                    continue
                e.set(name,value)

        if len(elements) == 0:
            # Add the node to other document
            #
            newElement = deepcopy(node)

            # Find the path to the parent
            #
            parent = node.getparent()
            path = convertXpath(parent)

            bParent = b.root.xpath(path)[0]
            bParent.append(newElement)

class XmlDoc:
    def __init__(self, xml):
        self.root = etree.fromstring(xml)
        self.tree = self.root.getroottree()

    def __str__(self):
        return etree.tostring(self.root, pretty_print=True)

    def nodes(self):
        return self.root.iter('*')



if __name__ == '__main__':
    a = XmlDoc(xmlA)
    b = XmlDoc(xmlB)

    mergeXml(a,b)
    print b

Это дает следующий результат:

<book>
    <chapter id="113">

        <sentence id="3">
            <word id="128168">
                <concept English="sadness"/>
            </word>
        </sentence>

        <sentence id="1" drums="Neil">
            <word id="128160" bass="Geddy">
                <concept English="joke"/>
            <POS Tag="V"/>
                <grammar type="STEM"/>
                <Aspect type="IMPV"/>
                <Number type="S"/>
            </word>
            <word id="128161">
                <concept English="romance"/>
            <POS Tag="V"/>
                <grammar type="STEM"/>
                <Aspect type="IMPF"/>
            </word>
        </sentence>

        <sentence id="2" guitar="Alex">
            <word id="128162">
                <concept English="happiness"/>
            <POS Tag="P"/>
                <grammar type="PREFIX"/>
                <Tag Tag="bi+"/>
            </word>
        </sentence>


    </chapter>
</book>
person shrewmouse    schedule 20.01.2016