Я застрял с XML и Python. Задача проста, но я так и не смог ее решить и потратил столько времени. Я пришел сюда за советом, как решить эту проблему с помощью нескольких строк.
Спасибо за любую помощь в обходе дерева. Я всегда получал слишком много или слишком мало элементов. Элементы могут быть вложены без ограничений. Данный пример является только примером. Я приму любое решение, не придирчивое к dom, minidom, sax, к чему угодно.
У меня есть XML-файл, похожий на этот:
<root>
<elm>
<elm>Common content</elm>
<elm xmlns="http://example.org/ns">
<elm lang="en">Content EN</elm>
<elm lang="cs">žluťoučký koníček</elm>
</elm>
<elm xml:id="abc123">Common content</elm>
<elm lang="en">Content EN</elm>
<elm lang="cs">Content CS</elm>
<elm lang="en">
<elm>Content EN</elm>
<elm>Content EN</elm>
</elm>
<elm lang="cs">
<elm>Content CS</elm>
<elm>Content CS</elm>
</elm>
</elm>
</root>
Что мне нужно - разобрать XML и написать новый файл. Новый файл должен содержать все элементы для данного языка и элементы без атрибута lang
.
Для языка «cs» выходной файл должен содержать это:
<root>
<elm>
<elm>Common content</elm>
<elm xmlns="http://example.org/ns">
<elm lang="cs">žluťoučký koníček</elm>
</elm>
<elm xml:id="abc123">Common content</elm>
<elm lang="cs">Content CS</elm>
<elm lang="cs">
<elm>Content CS</elm>
<elm>Content CS</elm>
</elm>
</elm>
</root>
Если вы можете сделать это, чтобы опустить атрибут lang
в новом файле, даже лучше. Но это не так важно.
UPDATE1: Добавлены символы юникода и атрибут пространства имен.
UPDATE2: Использование Python 2.5, предпочтительнее для стандартных библиотек.
Использование lxml:
import lxml.etree as le
with open('doc.xml','r') as f:
doc=le.parse(f)
for elem in doc.xpath('//*[attribute::lang]'):
if elem.attrib['lang']=='en':
elem.attrib.pop('lang')
else:
parent=elem.getparent()
parent.remove(elem)
print(le.tostring(doc))
yields
<root>
<elm>Common content</elm>
<elm>
<elm>Content EN</elm>
</elm>
<elm>Common content</elm>
<elm>Content EN</elm>
<elm>
<elm>Content EN</elm>
<elm>Content EN</elm>
</elm>
</root>
Я не уверен, как лучше всего удалить атрибут lang
, но вот код, который выполняет другие изменения (Python 2.7; для 2.5 или 2.6 используйте getIterator
вместо iter
), предполагая, что при удалении элемента вы также всегда хотите удалить все, что содержится в этом элементе.
Этот код просто выводит результат на стандартный вывод (конечно, вы можете перенаправить его по своему усмотрению или напрямую записать в какой-нибудь новый файл и т. д.):
import sys
from xml.etree import cElementTree as et
def picklang(path, lang='en'):
tr = et.parse(path)
for element in tr.iter():
for subelement in element:
la = subelement.get('lang')
if la is not None and la != lang:
element.remove(subelement)
return tr
if __name__ == '__main__':
tr = picklang('la.xml')
tr.write(sys.stdout)
print
С la.xml
в вашем примере это пишет
<root>
<elm>Common content</elm>
<elm>
<elm lang="en">Content EN</elm>
</elm>
<elm>Common content</elm>
<elm lang="en">Content EN</elm>
<elm lang="en">
<elm>Content EN</elm>
<elm>Content EN</elm>
</elm>
</root>