Scalanie plików xml z zagnieżdżonymi elementami bez zewnętrznych bibliotek
Próbuję połączyć wiele plików XML razem za pomocą Pythona i bez zewnętrznych bibliotek. Pliki XML mają zagnieżdżone elementy.
Przykładowy Plik 1:
<root>
<element1>textA</element1>
<elements>
<nested1>text now</nested1>
</elements>
</root>
Przykładowy Plik 2:
<root>
<element2>textB</element2>
<elements>
<nested1>text after</nested1>
<nested2>new text</nested2>
</elements>
</root>
Czego Chcę:
<root>
<element1>textA</element1>
<element2>textB</element2>
<elements>
<nested1>text after</nested1>
<nested2>new text</nested2>
</elements>
</root>
Co próbowałem:
From 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)
Co Dostaję:
<root>
<element1>textA</element1>
<elements>
<nested1>text now</nested1>
</elements>
<element2>textB</element2>
<elements>
<nested1>text after</nested1>
<nested2>new text</nested2>
</elements>
</root>
Mam nadzieję, że widzisz i rozumiesz mój problem. Szukam odpowiedniego rozwiązania, wszelkich wskazówek byłoby cudownie.
Aby wyjaśnić problem, używając obecnego rozwiązania, które mam, zagnieżdżone elementy nie są scalane.
2 answers
To, co napisałeś, polega na łączeniu wszystkich elementów, niezależnie od tego, czy element z tym samym znacznikiem już istnieje. Więc trzeba iterację nad elementami i ręcznie sprawdzić i połączyć je w sposób, który uważasz za odpowiedni, ponieważ nie jest to standardowy sposób obsługi plików XML. Nie potrafię tego wyjaśnić lepiej niż Kod, więc oto jest, mniej lub bardziej skomentowane:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-14 16:23:46
Dziękuję, ale moim problemem było połączenie, biorąc pod uwagę również atrybuty. oto kod po moim patchu:
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-27 13:13:01