2017-06-14 28 views
2

我有一個需要讀取XML文件並用特定值替換字符串的需求。 XML包含CDATA元素,我需要保留它。 我曾嘗試使用解析器並將strip_data設置爲false。這不起作用,需要幫助才能找到實現它的方法。即使使用strip_cdata = False,CDATA也會被剝離爲lxml

import lxml.etree as ET 

parser1 = ET.XMLParser(strip_cdata=False) 

with open('testxml.xml', encoding="utf8") as f: 
tree = ET.parse(f, parser=parser1) 

root = tree.getroot() 
for elem in root.getiterator(): 
    try: 
     elem.text = elem.text.replace('Bundled Manager 2.2(8b)', '123456') 
    except AttributeError: 
     pass 

tree.write('output_new8.xml', xml_declaration=True, method='xml', encoding="utf8") 

下面是示例XML:


 <?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Copyright (c) 2015 Moto Company, LLC. All rights reserved. Moto Confidential/Proprietary Information --> 
<Benchmark> 
     <status date="2013-03-11">draft</status> 
    <title>Logitech TMM block(TM) System 300 Release Certification Matrix</title> 
    <description>Random discription</description> 
    <version time="2013-03-05T15:20:20.995-04:00" update="">3.0.0-2017.03.00</version> 
     <model system="urn:xccdf:scoring:default"/> 
    <Profile id="xccdf_com.Moto_profile_release_4.0.21"> 
     <status date="2016-03-30">draft</status> 
     <title>RCM 4.0.21</title> 
     <description><![CDATA[<p>Moto Vblock System 300 Release 4.0.21</p> 
<ul><li> TMM VNX OE for File was updated to 7.1.79.8.</li> 
</ul>]]> 
</description> 
     <set-value idref="xccdf_com.Moto_value_vision_content_version">3.0.0-2015.07.00</set-value> 
     <set-value idref="xccdf_com.Moto_value_vision_version">3.0.0</set-value> 
     <set-value idref="xccdf_com.Moto_value_vplex_version">5.3.0.03.00.04</set-value> 
     <set-value idref="xccdf_com.Moto_value_powerpath_version">Bundled Manager 2.2(8b)</set-value>  
     <select idref="xccdf_com.Moto_rule_vnx_version" selected="true"/> 
     <select idref="xccdf_com.Moto_rule_vplex_version" selected="true"/> 
    </Profile> 
</Benchmark> 

代碼的輸出如下所示:

<?xml version='1.0' encoding='UTF8'?> 
<!-- Copyright (c) 2015 Moto Company, LLC. All rights reserved. Moto Confidential/Proprietary Information --><Benchmark> 
    <status date="2013-03-11">draft</status> 
    <title>Logitech TMM block(TM) System 300 Release Certification Matrix</title> 
    <description>Random discription</description> 
    <version time="2013-03-05T15:20:20.995-04:00" update="">3.0.0-2017.03.00</version> 
     <model system="urn:xccdf:scoring:default"/> 
    <Profile id="xccdf_com.Moto_profile_release_4.0.21"> 
     <status date="2016-03-30">draft</status> 
     <title>RCM 4.0.21</title> 
     <description>&lt;p&gt;Moto Vblock System 300 Release 4.0.21&lt;/p&gt; 
&lt;ul&gt;&lt;li&gt; TMM VNX OE for File was updated to 7.1.79.8.&lt;/li&gt; 
&lt;/ul&gt; 
</description> 
     <set-value idref="xccdf_com.Moto_value_vision_content_version">3.0.0-2015.07.00</set-value> 
     <set-value idref="xccdf_com.Moto_value_vision_version">3.0.0</set-value> 
     <set-value idref="xccdf_com.Moto_value_vplex_version">5.3.0.03.00.04</set-value> 
     <set-value idref="xccdf_com.Moto_value_powerpath_version">123456</set-value>   
     <select idref="xccdf_com.Moto_rule_vnx_version" selected="true"/> 
     <select idref="xccdf_com.Moto_rule_vplex_version" selected="true"/> 
    </Profile> 
</Benchmark 

>

正如你可以看到,CDATA部分被剝離。 如果有人能幫助我,這將是一件好事。

回答

2

這是因爲你在做

elem.text = elem.text.replace('Bundled Manager 2.2(8b)', '123456') 

與普通文本節點替換CDATA。

documentation狀態

注意.text屬性不怎麼不給任何跡象表明該文本內容由CDATA部分包裹着。如果你想確保你的數據被CDATA塊包裝,你可以使用CDATA()文本包裝器。

因此,如果你想保持CDATA部分,你應該只分配給elem.text如果要修改它,並指示LXML使用CDATA節:

if 'Bundled Manager 2.2(8b)' in elem.text: 
    elem.text = ET.CDATA(elem.text.replace('Bundled Manager 2.2(8b)', '123456')) 

由於如何ElementTree庫工程(整個文本和cdata內容連接並在.text屬性中以str的形式公開),但實際上不可能知道CDATA是否最初被使用。 (見Figuring out where CDATA is in lxml element?the source code

+0

謝謝基思,你的解釋是有道理的。我會嘗試解決方案並讓你知道。 – Anky

+0

@Keith,我很好奇,如果你能分辨CDATA在空時是否被使用過? ''。我的預感是可以的,因爲lxml返回一個空字符串,而一個空節點通常返回'None'。 –

+0

@MarcelWilson我的測試確認你的直覺:)即使使用'remove_blank_text = True',CDATA仍然存在。 –

相關問題