如何用Python更新XML中多层子元素的指定节点值?
高效更新JMeter XML配置的Python实现方法
需求说明
- 为启用状态(
enabled="true")且测试名为"Aggregate Report"的ResultCollector节点,更新<stringProp name="filename">的值为UUID前缀+原文件名的格式(示例:c82fa39b-5079-4838-b17c-fdf85f49fd5f_language-preferences__GET.jtl) - 将上述节点下的
<subresults>元素值修改为true
原始XML片段
</hashTree> <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="false"> <boolProp name="ResultCollector.error_logging">false</boolProp> <objProp> <name>saveConfig</name> <value class="SampleSaveConfiguration"> <time>true</time> <latency>true</latency> <timestamp>true</timestamp> <success>true</success> <label>true</label> <code>true</code> <message>true</message> <threadName>true</threadName> <dataType>false</dataType> <encoding>false</encoding> <assertions>true</assertions> <subresults>false</subresults> <responseData>false</responseData> <samplerData>false</samplerData> <xml>false</xml> <fieldNames>true</fieldNames> <responseHeaders>false</responseHeaders> <requestHeaders>false</requestHeaders> <responseDataOnError>true</responseDataOnError> <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage> <assertionsResultsToSave>0</assertionsResultsToSave> <bytes>true</bytes> <hostname>true</hostname> <threadCounts>true</threadCounts> <sampleCount>true</sampleCount> </value> </objProp> <stringProp name="filename"></stringProp> </ResultCollector> <hashTree/> <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true"> <boolProp name="ResultCollector.error_logging">false</boolProp> <objProp> <name>saveConfig</name> <value class="SampleSaveConfiguration"> <time>true</time> <latency>true</latency> <timestamp>true</timestamp> <success>true</success> <label>true</label> <code>true</code> <message>true</message> <threadName>true</threadName> <dataType>true</dataType> <encoding>false</encoding> <assertions>true</assertions> <subresults>false</subresults> <responseData>false</responseData> <samplerData>false</samplerData> <xml>false</xml> <fieldNames>true</fieldNames> <responseHeaders>false</responseHeaders> <requestHeaders>false</requestHeaders> <responseDataOnError>true</responseDataOnError> <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage> <assertionsResultsToSave>0</assertionsResultsToSave> <bytes>true</bytes> <sentBytes>true</sentBytes> <url>true</url> <threadCounts>true</threadCounts> <idleTime>true</idleTime> <connectTime>true</connectTime> </value> </objProp> <stringProp name="filename">language-preferences__GET.jtl</stringProp> </ResultCollector> <hashTree/>
现有代码问题
原代码通过嵌套循环遍历子元素,导航多层节点的效率较低,且代码冗余:
import xml.etree.ElementTree as ET import uuid tree = ET.parse(jmx_path) aggregate_samplers = tree.findall('.//ResultCollector[@testname="Aggregate Report"]') active_aggregate_samplers = [active_aggregate_sampler for active_aggregate_sampler in aggregate_samplers if active_aggregate_sampler.attrib['enabled'] == 'true'] for active_sampler in active_aggregate_samplers: for child in active_sampler: if child.tag == 'objProp': # < update **subresults** value as true> elif child.attrib['name'] == 'filename': child.text = str(uuid.uuid4()) + '_' + child.text # new file name # < update **filename** value as c82fa39b-5079-4838-b17c-fdf85f49fd5f_language-preferences__GET.jtl>
优化后的代码实现
利用XPath直接定位目标节点,无需逐层遍历,代码更简洁高效:
import xml.etree.ElementTree as ET import uuid tree = ET.parse(jmx_path) # 直接筛选启用状态的Aggregate Report节点 active_samplers = tree.findall('.//ResultCollector[@testname="Aggregate Report" and @enabled="true"]') for sampler in active_samplers: # 更新filename:直接定位stringProp节点 filename_node = sampler.find('.//stringProp[@name="filename"]') if filename_node is not None and filename_node.text: filename_node.text = f"{str(uuid.uuid4())}_{filename_node.text}" elif filename_node is not None: # 处理原文件名为空的情况,可自定义默认文件名 filename_node.text = f"{str(uuid.uuid4())}_default_report.jtl" # 更新subresults:直接定位到SampleSaveConfiguration下的subresults节点 subresults_node = sampler.find('.//objProp/value[@class="SampleSaveConfiguration"]/subresults') if subresults_node is not None: subresults_node.text = "true" # 保存修改后的XML tree.write(jmx_path, encoding='utf-8', xml_declaration=True)
代码解释
- 节点筛选优化:通过XPath表达式
.//ResultCollector[@testname="Aggregate Report" and @enabled="true"]一步到位,直接获取符合条件的启用节点,无需二次过滤。 - filename节点定位:用
.//stringProp[@name="filename"]直接找到目标节点,避免遍历所有子元素。 - subresults节点定位:通过层级XPath
.//objProp/value[@class="SampleSaveConfiguration"]/subresults直接穿透多层结构,精准定位到<subresults>元素。 - 异常处理:增加了对节点不存在或原文本为空的判断,避免运行时报错。
内容的提问来源于stack exchange,提问作者Subrata Sarkar
相关产品推荐
相关产品推荐

