2013-02-14 27 views
11

Tôi đang cố hợp nhất nhiều tệp XML với nhau bằng Python và không có thư viện bên ngoài. Các tệp XML có các phần tử lồng nhau.Hợp nhất các tệp xml với các phần tử lồng nhau mà không có thư viện bên ngoài

mẫu file 1:

<root> 
    <element1>textA</element1> 
    <elements> 
    <nested1>text now</nested1> 
    </elements> 
</root> 

mẫu file 2:

<root> 
    <element2>textB</element2> 
    <elements> 
    <nested1>text after</nested1> 
    <nested2>new text</nested2> 
    </elements> 
</root> 

Những gì tôi muốn:

<root> 
    <element1>textA</element1>  
    <element2>textB</element2> 
    <elements> 
    <nested1>text after</nested1> 
    <nested2>new text</nested2> 
    </elements> 
</root> 

Những gì tôi đã thử:

Từ this answer.

from xml.etree import ElementTree as et 
def combine_xml(files): 
    first = None 
    for filename in files: 
     data = et.parse(filename).getroot() 
     if first is None: 
      first = data 
     else: 
      first.extend(data) 
    if first is not None: 
     return et.tostring(first) 

Những gì tôi có được:

<root> 
    <element1>textA</element1> 
    <elements> 
    <nested1>text now</nested1> 
    </elements> 
    <element2>textB</element2> 
    <elements> 
    <nested1>text after</nested1> 
    <nested2>new text</nested2> 
    </elements> 
</root> 

Tôi hy vọng bạn có thể xem và hiểu vấn đề của tôi. Tôi đang tìm một giải pháp thích hợp, mọi hướng dẫn sẽ thật tuyệt vời.

Để làm rõ vấn đề, sử dụng giải pháp hiện tại mà tôi có, các phần tử lồng nhau không được hợp nhất.

Trả lời

18

Mã bạn đăng đang làm là kết hợp tất cả các phần tử bất kể phần tử có cùng một thẻ đã tồn tại hay không. Vì vậy, bạn cần phải lặp qua các phần tử và kiểm tra thủ công và kết hợp chúng theo cách bạn thấy phù hợp, bởi vì nó không phải là một cách tiêu chuẩn để xử lý các tệp XML. Tôi không thể giải thích nó tốt hơn so với mã, do đó, ở đây nó là, nhiều hơn hoặc ít hơn bình luận:

from xml.etree import ElementTree as et 

class XMLCombiner(object): 
    def __init__(self, filenames): 
     assert len(filenames) > 0, 'No filenames!' 
     # save all the roots, in order, to be processed later 
     self.roots = [et.parse(f).getroot() for f in filenames] 

    def combine(self): 
     for r in self.roots[1:]: 
      # combine each element with the first one, and update that 
      self.combine_element(self.roots[0], r) 
     # return the string representation 
     return et.tostring(self.roots[0]) 

    def combine_element(self, one, other): 
     """ 
     This function recursively updates either the text or the children 
     of an element if another element is found in `one`, or adds it 
     from `other` if not found. 
     """ 
     # Create a mapping from tag name to element, as that's what we are fltering with 
     mapping = {el.tag: el for el in one} 
     for el in other: 
      if len(el) == 0: 
       # Not nested 
       try: 
        # Update the text 
        mapping[el.tag].text = el.text 
       except KeyError: 
        # An element with this name is not in the mapping 
        mapping[el.tag] = el 
        # Add it 
        one.append(el) 
      else: 
       try: 
        # Recursively process the element, and update it in the same way 
        self.combine_element(mapping[el.tag], el) 
       except KeyError: 
        # Not in the mapping 
        mapping[el.tag] = el 
        # Just add it 
        one.append(el) 

if __name__ == '__main__': 
    r = XMLCombiner(('sample1.xml', 'sample2.xml')).combine() 
    print '-'*20 
    print r 
+0

Hoạt động hoàn hảo, cảm ơn, tôi vừa mới bắt đầu viết mã của riêng mình. :) –

+0

Rất tốt, cảm ơn. Chúng tôi cũng cần hợp nhất các thuộc tính. Nó có thể được thực hiện bằng cách thêm 'one.attrib.update (other.attrib)' vào lúc bắt đầu 'combined_element' và' mapping [el.tag] .attrib.update (el.attrib) 'sau khi thay thế văn bản phần tử. –

+0

Ồ đúng, tôi đã quên mất các thuộc tính. Nắm bắt tốt. – jadkik94

2

Cảm ơn bạn, nhưng vấn đề của tôi là hợp nhất bằng cách xem xét các thuộc tính cũng có. đây là mã sau khi bản vá của tôi:

import sys 
    from xml.etree import ElementTree as et 


    class hashabledict(dict): 
     def __hash__(self): 
      return hash(tuple(sorted(self.items()))) 


    class XMLCombiner(object): 
     def __init__(self, filenames): 
      assert len(filenames) > 0, 'No filenames!' 
      # save all the roots, in order, to be processed later 
      self.roots = [et.parse(f).getroot() for f in filenames] 

    def combine(self): 
     for r in self.roots[1:]: 
      # combine each element with the first one, and update that 
      self.combine_element(self.roots[0], r) 
     # return the string representation 
     return et.ElementTree(self.roots[0]) 

    def combine_element(self, one, other): 
     """ 
     This function recursively updates either the text or the children 
     of an element if another element is found in `one`, or adds it 
     from `other` if not found. 
     """ 
     # Create a mapping from tag name to element, as that's what we are fltering with 
     mapping = {(el.tag, hashabledict(el.attrib)): el for el in one} 
     for el in other: 
      if len(el) == 0: 
       # Not nested 
       try: 
        # Update the text 
        mapping[(el.tag, hashabledict(el.attrib))].text = el.text 
       except KeyError: 
        # An element with this name is not in the mapping 
        mapping[(el.tag, hashabledict(el.attrib))] = el 
        # Add it 
        one.append(el) 
      else: 
       try: 
        # Recursively process the element, and update it in the same way 
        self.combine_element(mapping[(el.tag, hashabledict(el.attrib))], el) 
       except KeyError: 
        # Not in the mapping 
        mapping[(el.tag, hashabledict(el.attrib))] = el 
        # Just add it 
        one.append(el) 

if __name__ == '__main__': 

    r = XMLCombiner(sys.argv[1:-1]).combine() 
    print '-'*20 
    print et.tostring(r.getroot()) 
    r.write(sys.argv[-1], encoding="iso-8859-1", xml_declaration=True) 
Các vấn đề liên quan