python lxml 根据子元素文本函数将 sub_element 添加到父项与 class

python lxml add sub_element to parent based on subelement text function vs. class

抱歉,这有点啰嗦,但我想尽可能详细。

我有以下示例 xml 文件:

<root>
 <input_file>
   <type>x</type>
 </input_file>
 <input_file>
   <type>y</type>
 </input_file>
</root>

并希望使用 python3.9 中的 python lxml 包添加基于 <type> 标签的子元素,以便 xml 文件看起来像这样:

<root>
 <input_file>
   <type>x</type>
   <path>hi</path>
 </input_file>
 <input_file>
   <type>y</type>
   <path>hi_again</path>
 </input_file>
</root>

以下代码有效:

from lxml import etree as LET

xml_file = 'test.xml'
tree = LET.parse(xml_file)
root = tree.getroot()

for input_file in root.findall('input_file'):

    type_element = input_file.find('type')

    if type_element.text == 'x':

        c = LET.SubElement(input_file, 'path')
        c.text = 'hi'

    elif type_element.text == 'y':

        c = LET.SubElement(input_file, 'path')
        c.text = 'hi_again'

LET.indent(root, space="  ")
tree.write(xml_file)

当我尝试从以下 class 执行此操作时,我创建了(文件 XMLReader.py):

from lxml import etree as LET
import string

class XMLReader(object):

    def __init__(self, file_path):

        self.file_path = file_path
        self.tree = LET.parse(self.file_path)
        self.root = self.tree.getroot()

    def set_sub_element(self, parent_tag, tag, info):

        child = LET.SubElement(parent_tag, tag)
        child.text = self.clean_string(info)
        self.root.append(child)
        LET.indent(self.root, space="  ")
        self.tree.write(self.file_path)

    def get_all_elements(self, tag):

        try:
            return self.root.findall(tag)

        except AttributeError:

            return None

    def clean_string(self, s):

        return ''.join(filter(lambda x: x in string.printable, s))

使用以下代码:


from XMLReader import XMLReader

items = {'x':'hi', 'y': 'hi_again'}
xml_file = 'test.xml'
xml_test = XMLReader(xml_file)
input_file_tags = xml_test.get_all_elements('input_file')

for input_file in input_file_tags:

    type_element = input_file.find('type')

    if type_element.text in items:

        item = items[type_element.text]

        xml_test.set_sub_element(input_file, 'path', item)

我得到以下结果文件:


<root>
  <input_file>
    <type>x</type>
  </input_file>
  <input_file>
    <type>y</type>
  </input_file>
  <path>hi</path>
  <path>hi_again</path>
</root>

我想知道我在这里做错了什么以获得与上面相同的结果,其中生成的 <path></path> 不是 <input_file></input_file> based on the <type></type> 值的子元素。

作为练习,这里有一个相对简单的方法:

#start with your `items`
items = {'x':'hi', 'y': 'hi_again'}
tags = ['input_file','type']
for v in items.keys():
    destination = root.xpath(f'//{tags[0]}/{tags[1]}[text()="{v}"]/..')[0]
    new_elem=LET.fromstring(f'<path>{items[v]}</path>')
    destination.insert(1,new_elem)
LET.indent(root, space="  ")
print(LET.tostring(root).decode())

输出:

<root>
  <input_file>
    <type>x</type>
    <path>hi</path>
  </input_file>
  <input_file>
    <type>y</type>
    <path>hi_again</path>
  </input_file>
</root>