使用 lxml 获取特定元素属性

Getting specific elements attribute with lxml

我有以下 xml 文件,我需要到达特定块并更改 2 个属性值。

xml 文件 - https://alvinalexander.com/java/jwarehouse/activemq/assembly/src/release/conf/jetty.xml.shtml

我在本地有。

我需要编辑的块:

<bean class="org.eclipse.jetty.webapp.WebAppContext">
                <property name="contextPath" value="/admin" />
                <property name="resourceBase" value="${activemq.home}/webapps/admin" />
                <property name="logUrlOnStart" value="true" />
            </bean>

我需要更改属性值<property name="contextPath" value="/admin" />

<property name="resourceBase" value="${activemq.home}/webapps/admin" />

<property name="contextPath" value="/hawtio" />

<property name="resourceBase" value="${activemq.home}/webapps/hawtio" />

我还需要不使用索引来做,我需要使用 xpath 来做。

这意味着,我希望使用 xpath 到达特定元素并更改其属性 不使用(例如)tree[i]

我的 python 代码(或至少我希望做的)

import lxml
from lxml import etree

#Initialise xml file , xml file has namespaces
tree = etree.parse("jetty.xml")
myroot = tree.getroot()

#XML File has atleast 5 childs to each element
#From here i expect to edit attributes without using
#Manual indexing , for example

#prop = myroot.find('property[@name="contextPath"]')
#prop.attrib['value'] = '/hawtio'
#prop = myroot.find(('./property[@name="resourceBase"]'))
#prop.attrib['value'] = "${activemq.home}/webapps/hawtio"
#etree.write("jetty.xml", pretty_print=True)

同样,我正在解析一个包含命名空间和大量子元素的文件,运行 find() returns None 和 运行 findall() returns 一个空列表,运行 那些带有命名空间的选项也 returns 空列表 \ None

进一步解释 - 打开一个 xml 文件,找到特定元素并编辑它们的属性 NO MATTER 它们的索引(例如 myroot[0][2])和将更改写回文件。

干杯,

试试下面的方法(没有使用外部库)

import xml.etree.ElementTree as ET


xml = '''<bean class="org.eclipse.jetty.webapp.WebAppContext">
                <property name="contextPath" value="/admin" />
                <property name="resourceBase" value="${activemq.home}/webapps/admin" />
                <property name="logUrlOnStart" value="true" />
            </bean>'''
            
root = ET.fromstring(xml)
prop = root.find('./property[@name="contextPath"]')
prop.attrib['value'] = 'new_value_goes_here'
ET.dump(root)

输出

<bean class="org.eclipse.jetty.webapp.WebAppContext">
                <property name="contextPath" value="new_value_goes_here" />
                <property name="resourceBase" value="${activemq.home}/webapps/admin" />
                <property name="logUrlOnStart" value="true" />
            </bean>

使用 baldermans answer 我找到了解决方法。

import lxml
from lxml import etree 

#Initialise xml file
tree = etree.parse("jetty.xml")
myroot = tree.getroot()

test = myroot.find('.//*[@name="contextPath"]')
print(test)

产量:

Element {http://www.springframework.org/schema/beans}property at

获取属性:

print(test.attrib)
{'name': 'contextPath', 'value': '/admin'}

更改值:

prop = myroot.find('.//*[@name="contextPath"]')
prop.attrib['value'] = '/hawtio'
prop = myroot.find(('.//*[@name="resourceBase"]'))
prop.attrib['value'] = "${activemq.home}/webapps/hawtio"
etree.write("jetty.xml", pretty_print=True)

我在 find()[@name="contextPath"] 上使用了 .//* 选项 因为我知道元素 contextPath 只出现过一次 因此,如果将来在文件中添加另一个 contextPath 元素,我的解决方案就不是很好,如果您找到比我更好的解决方案,请 post 它。

编辑:

刚刚注意到文件已更新,这里是更新

prop = myroot.findall('.//*[@name="contextPath"]')
for i in prop:
     if '/admin' in i.attrib.itervalues():
          prop.attrib['value'] = '/hawtio'
          
prop = myroot.findall(('.//*[@name="resourceBase"]'))
for i in prop:
     if '${activemq.home}/webapps/admin' in i.attrib.itervalues():
          prop.attrib['value'] = '${activemq.home}/webapps/hawtio'

由于属性作为键值返回,我们可以使用内置字典 itervalues() 函数来定位特定键。