使用 lxml 修改具有 child 元素值的 xml 属性元素
Modifying an xml attribute element with a value of a child element with lxml
我有这样一个 XML 片段:
<parent id="1">
<child1>
<child2>[content]I need to get[/content]Other text</child2>
</child1>
</parent>
我想将 "child1" 的 [content] 作为属性添加到 parent 元素中。
得到这样的东西:
<parent id="1" value = "I need to get">
<child1>
<child2>Other text</child2>
</child1>
</parent>
我有这段代码,但它不起作用,因为它看起来只在第一个 child 中迭代,不会转到下一个。
pattern = re.compile('[content](.*?)[/content]')
xml_parser = et.parse(str(xml_file))
root_xml = xml_parser.getroot()
translatable_elements = root_xml.xpath('//parent')
for element in translatable_elements:
for child_element in element.iterchildren():
if child_element.tag == 'child1':
source_content = child_element.text
value_str = pattern.match(source_content).group(1)
element.attrib['value'] = value_str
source_content = pattern.sub(source_content,'')
tree = et.ElementTree(root_xml)
tree.write(str(xml_file), encoding='utf-8', pretty_print=True)
您需要使用正确的正则表达式转义字符串编译 re
。此外,您试图从 child1
而不是 child2
获取文本。这应该与您正在寻找的路线一致:
import re
from lxml import etree
with open(path, 'r') as f:
tree = etree.parse(f)
pattern = re.compile(r'\[content\](.*?)\[\/content\]')
root = tree.getroot()
pars = root.xpath('//parent')
for par in pars:
for child1 in par.iterchildren('child1'):
child2 = child1.getchildren()[0]
val = pattern.match(child2.text).group(1)
par.set('value', val)
child2.text = pattern.sub('', child2.text)
print(etree.tostring(tree, encoding='utf-8', pretty_print=True))
另一种选择是完全不使用正则表达式并使用普通的 xpath。
既然你说你的 XML 是一个片段,我将它包装在一个 doc
元素中并添加另一个 parent
来显示当有多个时会发生什么。
示例...
XML 输入 (input.xml)
<doc>
<parent id="1">
<child1>
<child2>[content]I need to get[/content]Other text</child2>
</child1>
</parent>
<parent id="2">
<child1>
<child2>[content]I need to get this too[/content]More other text</child2>
</child1>
</parent>
</doc>
Python
from lxml import etree
tree = etree.parse("input.xml")
for parent in tree.xpath(".//parent"):
child2 = parent.xpath("./child1/child2")[0]
parent.attrib["value"] = child2.xpath("substring-before(substring-after(.,'[content]'),'[/content]')")
child2.text = child2.xpath("substring-after(.,'[/content]')")
tree.write("output.xml")
输出 (output.xml)
<doc>
<parent id="1" value="I need to get">
<child1>
<child2>Other text</child2>
</child1>
</parent>
<parent id="2" value="I need to get this too">
<child1>
<child2>More other text</child2>
</child1>
</parent>
</doc>
我有这样一个 XML 片段:
<parent id="1">
<child1>
<child2>[content]I need to get[/content]Other text</child2>
</child1>
</parent>
我想将 "child1" 的 [content] 作为属性添加到 parent 元素中。
得到这样的东西:
<parent id="1" value = "I need to get">
<child1>
<child2>Other text</child2>
</child1>
</parent>
我有这段代码,但它不起作用,因为它看起来只在第一个 child 中迭代,不会转到下一个。
pattern = re.compile('[content](.*?)[/content]')
xml_parser = et.parse(str(xml_file))
root_xml = xml_parser.getroot()
translatable_elements = root_xml.xpath('//parent')
for element in translatable_elements:
for child_element in element.iterchildren():
if child_element.tag == 'child1':
source_content = child_element.text
value_str = pattern.match(source_content).group(1)
element.attrib['value'] = value_str
source_content = pattern.sub(source_content,'')
tree = et.ElementTree(root_xml)
tree.write(str(xml_file), encoding='utf-8', pretty_print=True)
您需要使用正确的正则表达式转义字符串编译 re
。此外,您试图从 child1
而不是 child2
获取文本。这应该与您正在寻找的路线一致:
import re
from lxml import etree
with open(path, 'r') as f:
tree = etree.parse(f)
pattern = re.compile(r'\[content\](.*?)\[\/content\]')
root = tree.getroot()
pars = root.xpath('//parent')
for par in pars:
for child1 in par.iterchildren('child1'):
child2 = child1.getchildren()[0]
val = pattern.match(child2.text).group(1)
par.set('value', val)
child2.text = pattern.sub('', child2.text)
print(etree.tostring(tree, encoding='utf-8', pretty_print=True))
另一种选择是完全不使用正则表达式并使用普通的 xpath。
既然你说你的 XML 是一个片段,我将它包装在一个 doc
元素中并添加另一个 parent
来显示当有多个时会发生什么。
示例...
XML 输入 (input.xml)
<doc>
<parent id="1">
<child1>
<child2>[content]I need to get[/content]Other text</child2>
</child1>
</parent>
<parent id="2">
<child1>
<child2>[content]I need to get this too[/content]More other text</child2>
</child1>
</parent>
</doc>
Python
from lxml import etree
tree = etree.parse("input.xml")
for parent in tree.xpath(".//parent"):
child2 = parent.xpath("./child1/child2")[0]
parent.attrib["value"] = child2.xpath("substring-before(substring-after(.,'[content]'),'[/content]')")
child2.text = child2.xpath("substring-after(.,'[/content]')")
tree.write("output.xml")
输出 (output.xml)
<doc>
<parent id="1" value="I need to get">
<child1>
<child2>Other text</child2>
</child1>
</parent>
<parent id="2" value="I need to get this too">
<child1>
<child2>More other text</child2>
</child1>
</parent>
</doc>