[英]How do I parse and write XML using Python's ElementTree without moving namespaces around?
我們的項目來自這種形式的上游XML:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<appSettings>
<add key="foo" value="default">
...
</appSettings>
</configuration>
然后它使用ElementTree讀取/解析此XML,然后對於匹配某個鍵(“foo”)的每個app設置,它會寫入一個新值, 它知道上游進程沒有(在這種情況下鍵為“foo”)應該有值“bar”)。
使用過濾后的XML的下游進程是aaahhhh ...... 脆弱的 。 它期望接收XML在上面那樣的形式。
如果我在沒有注冊命名空間的情況下解析這個XML,那么ElementTree就會在輸入上修改我的樹:
<configuration xmlns:ns0="urn:schemas-microsoft-com:asm.v1">
<runtime>
<ns0:assemblyBinding>
<ns0:dependentAssembly>
<ns0:assemblyIdentity culture="neutral" name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
<ns0:bindingRedirect newVersion="7.0.0.0" oldVersion="0.0.0.0-6.0.0.0" />
</ns0:dependentAssembly>
</ns0:assemblyBinding>
</runtime>
<appSettings>
<add key="foo" value="default">
...
</appSettings>
</configuration>
下游流程無法解決這個問題,因為從語義上講,這並不是一件容易理解的事情。 所以,我決定注冊我知道上游進程將提供的命名空間作為默認命名空間,以避免前綴出現在任何地方,現在我得到這個:
<configuration xmlns="urn:schemas-microsoft-com:asm.v1">
<runtime>
<assemblyBinding>
<dependentAssembly>
<assemblyIdentity culture="neutral" name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect newVersion="7.0.0.0" oldVersion="0.0.0.0-6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<appSettings>
<add key="foo" value="default">
...
</appSettings>
</configuration>
我對XML知之甚少,但這也是下游組件的吶喊,而且在我看來,現在並不意味着這個默認的xmlns
現在適用於<configuration>
所有包含元素,而之前它只應用於<assemblyBinding>
元素?
無論如何, 使用ElementTree來處理這個命名空間,以便我可以接受上游的XML,設置foo
的值,然后在下游傳遞它,而不移動命名空間,並完全按照我發現的那樣離開它?
我可以使用一個基於lxml的解決方案,它似乎處理這個問題, 但是 ,lxml依賴於C,下游組件真的不想支持它:純Python解決方案更可取。
我可以將文檔讀作HTML,忽略命名空間屬性,讓我操縱我想要的值,然后傳遞文檔; 但是 ,我還沒有找到一個不會包含所有元素名稱的Python解析器,而我的下游組件需要保留所有元素名稱的大小寫。
我可以使用字符串解析和正則表達式。 我寧願不寫我自己的解析器。
到目前為止我在ElementTree中找到關於名稱空間處理的唯一建議是建議“注冊默認名稱空間以避免前綴”方法,我認為這種方法是合適的,但ElementTree則堅持在轉儲時將xmlns
聲明移動到根節點。
我也可以巧妙地建立一個字符串,將樹分階段轉儲出來,並按照正確的順序將xmlns
聲明放回“正確的節點”,但這也讓我感到非常脆弱。
有沒有人設法通過這樣的問題?
據我所知,更適合您需求的解決方案是使用xml.etree.ElementTree
公開的功能編寫純Python自定義呈現。 這是一個可能的解決方案:
from xml.etree import ElementTree as ET
from re import findall, sub
def render(root, buffer='', namespaces=None, level=0, indent_size=2, encoding='utf-8'):
buffer += f'<?xml version="1.0" encoding="{encoding}" ?>\n' if not level else ''
root = root.getroot() if isinstance(root, ET.ElementTree) else root
_, namespaces = ET._namespaces(root) if not level else (None, namespaces)
for element in root.iter():
indent = ' ' * indent_size * level
tag = sub(r'({[^}]+}\s*)*', '', element.tag)
buffer += f'{indent}<{tag}'
for ns in findall(r'{[^}]+}', element.tag):
ns_key = ns[1:-1]
if ns_key not in namespaces: continue
buffer += ' xmlns' + (f':{namespaces[ns_key]}' if namespaces[ns_key] != '' else '') + f'="{ns_key}"'
del namespaces[ns_key]
for k, v in element.attrib.items():
buffer += f' {k}="{v}"'
buffer += '>' + element.text.strip() if element.text else '>'
children = list(element)
for child in children:
sep = '\n' if buffer[-1] != '\n' else ''
buffer += sep + render(child, level=level+1, indent_size=indent_size, namespaces=namespaces)
buffer += f'{indent}</{tag}>\n' if 0 != len(children) else f'</{tag}>\n'
return buffer
通過將您提供的XML
數據發布到上面的render
函數,如下所示:
data=\
'''<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<appSettings>
<add key="foo" value="default" />
</appSettings>
</configuration>'''
e = ET.fromstring(data)
ET.register_namespace('', "urn:schemas-microsoft-com:asm.v1")
r = ET.ElementTree(e)
您將獲得以下生成的XML
,其中包含您聲明要查找的屬性:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"></assemblyIdentity>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="7.0.0.0"></bindingRedirect>
</dependentAssembly>
</assemblyBinding>
</runtime>
<appSettings>
<add key="foo" value="default"></add>
</appSettings>
</configuration>
我知道我來參加晚會..無論如何希望這會幫助你和許多其他人有同樣的問題,這里是一個很好的解決方案。 快樂的編碼!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.