Как правильно использовать API xmlfile из lxml - PullRequest
0 голосов
/ 11 сентября 2018

У меня есть большой (5+ гигабайт) XML-файл, который мне нужно проанализировать, выполнить некоторые операции и написать новый XML-файл.

dummy.xml

<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://www.namespace.com" catalog-id="test-catalog">
    <header>
        <name>Product Catalog</name>
    </header>

    <product product-id="1234">
        <available-flag>false</available-flag>
        <name>product1</name>
    </product>
    <product product-id="5678">
        <available-flag>false</available-flag>
        <name>product1</name>
    </product>
    <product product-id="9999">
        <available-flag>false</available-flag>
        <name>product1</name>
    </product>    
</catalog>

Как высм. выше XML имеет 3 product тегов, и мне нужно отфильтровать некоторые идентификаторы продуктов на основе предварительно определенного списка идентификаторов.

Я использую lxml iterparse для итеративного анализа XML и хочуиспользовать xmlfile API для создания нового XML в пошаговом режиме, чтобы сохранить объем памяти.Итак, мой мотив состоит в том, чтобы отфильтровать теги продукта, которые не соответствуют критериям, и скопировать остальные теги XML, как есть.

from lxml import etree
f = './dummy.xml'

f1 = './test.xml'
context = etree.iterparse(f, events=('start',))
productsToExport = ['1234']


with etree.xmlfile(f1, encoding='utf-8') as xf:
    xf.write_declaration()
    with xf.element('catalog xmlns="http://www.namespace.com" catalog-id="test-catalog"'):
        for event, element in context:
            tagName = etree.QName(element.tag).localname
            if (tagName == 'product'):
                pid = element.get('product-id')
                if (pid in productsToExport):
                    xf.write(element)
            elif (tagName == 'header'):
                xf.write(element) # copy existing header tag as it is

Над кодом работает нормально и генерирует XML, как показано ниже

<?xml version='1.0' encoding='utf-8'?>
<catalog xmlns="http://www.namespace.com" catalog-id="test-catalog">
    <header xmlns="http://www.namespace.com">
        <name>Product Catalog</name>
    </header>

    <product xmlns="http://www.namespace.com" product-id="1234">
        <available-flag>false</available-flag>
        <name>product1</name>
    </product>
</catalog xmlns="http://www.namespace.com" catalog-id="test-catalog">

Если вы наблюдаете вышеупомянутый XML, у него есть несколько проблем:

  • Закрывающий тег <catalog> содержит xmlns & catalog-id в нем
  • Все теги, такие как header, product, имеют атрибут xmlns, присутствующий в нем

Я проверил Документация API xmlfile , но не смог найти способ исправить вышеуказанные проблемы.

РЕДАКТИРОВАТЬ:

Мне удалось решить 1-й вопрос, используя ниже

attribs = {'xmlns' : 'http://www.namespace.com', 'catalog-id' : 'test-catalog'}
 with xf.element('catalog', attribs):
     # previous logic

Итак, теперь мне осталось удалить пространство имен из каждого элемента.

1 Ответ

0 голосов
/ 12 сентября 2018

Попробуйте просто перестроить дерево XML с помощью методов lxml.etree вместо xmlfile API, все еще в контексте вашего iterparse:

from lxml import etree

f = './dummy.xml'
f1 = './test.xml'    
productsToExport = ['1234']

# ROOT ELEMENT WITH DEFUALT NAMESPACE
my_nmsp = {None: 'http://www.namespace.com'}

# INITIALIZE ITERATOR
context = etree.iterparse(f, events=('start',))

for event, element in context:    
    tagName = etree.QName(element.tag).localname   

    for prod in productsToExport:
        root = etree.Element('catalog', nsmap=my_nmsp)
        root.text = '\n\t'
        root.attrib['catalog-id'] = "test-catalog"

        # PRODUCT ELEMENT
        if tagName == 'product':
            pid = element.get('product-id')

            if pid == prod:
                root.append(element)

        # HEADER ELEMENT        
        elif (tagName == 'header'):
            root.append(element)

        # OUTPUT TREE TO FILE
        with open(f1, 'wb') as f:
            f.write(etree.tostring(root, pretty_print=True))
* 1006 1007 * ** Выход * 1008 1009 * **
<catalog xmlns="http://www.namespace.com" catalog-id="test-catalog">
    <header>
        <name>Product Catalog</name>
    </header>

    <product product-id="1234">
        <available-flag>false</available-flag>
        <name>product1</name>
    </product> 
</catalog>
...