обединяване на 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>

file2.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>

Добре, опитах да направя това в path, но не получих желания резултат:

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 заявка в моите файлове? как да сравните, ако двата файла съдържат една и съща дума ID, след това копирайте и създайте нов xml файл? - person spring rose; 26.07.2013
comment
добре. това са отделни въпроси. попитахте как да обедините два xml файла. за вашата xpath заявка, бих погледнал тук: документи .python.org/2/library/ От гледна точка на вашето сравнение на word-id, ще трябва да изпълните xpath заявката, за да получите списък със съвпадащи възли, да го повторите и да сравните думата id и дали е не във вашия нов 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“ няма атрибут „element“ - 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:

  • барабани='Нийл'
  • bass='Geddy'

След това към B добавих:

  • guitar='Алекс'

Окончателният обединен документ включва и тримата членове на силовото трио.

Добавих също <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