如何正确使用lxml中的xmlfile API

时间:2018-09-11 15:46:18

标签: python xml python-3.x lxml

我有一个大文件(超过5个演出),需要解析,做一些操作并编写一个新的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标签,我需要根据预定义的ID列表来过滤一些产品ID。

我正在使用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>标签中包含xmlnscatalog-id
  • 诸如header, product之类的所有标签都具有xmlns属性

我检查了xmlfile api documentation,但找不到解决上述问题的方法。

编辑:

我设法通过使用以下方法解决了第一个问题

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

因此,现在剩下的是从每个元素中删除名称空间。

1 个答案:

答案 0 :(得分:0)

即使在您的lxml.etree上下文中,也可以考虑使用xmlfile方法而不是iterparse API重建XML树:

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))

输出

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