【问题标题】:Saving XML using ETree in Python. It's not retaining namespaces, and adding ns0, ns1 and removing xmlns tags在 Python 中使用 ETree 保存 XML。它不保留命名空间,并添加 ns0、ns1 和删除 xmlns 标签
【发布时间】:2015-10-26 15:23:18
【问题描述】:

我看到这里有类似的问题,但没有什么能完全帮助我。 我还查看了有关名称空间的官方文​​档,但找不到任何真正帮助我的东西,也许我对 XML 格式太陌生了。 我知道也许我需要创建自己的命名空间字典?无论哪种方式,这是我的情况:

我从一个 API 调用中得到一个结果,它给了我一个 XML,它作为一个字符串存储在我的 Python 应用程序中。

我想要完成的只是抓住这个 XML,换出一个很小的值(b:string 值用户 ConditionValue/Default 但这与这个问题无关) 然后将其保存为字符串,以便稍后在 Rest POST 调用中发送。

源 XML 如下所示:

<Context xmlns="http://Test.the.Sdk/2010/07" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<xmlns i:nil="true" xmlns="http://schema.test.org/2004/07/Test.Soa.Vocab" xmlns:a="http://schema.test.org/2004/07/System.Xml.Serialize"/>
<Conditions xmlns:a="http://schema.test.org/2004/07/Test.Soa.Vocab">
    <a:Condition>
        <a:xmlns i:nil="true" xmlns:b="http://schema.test.org/2004/07/System.Xml.Serialize"/>
        <Identifier>a23aacaf-9b6b-424f-92bb-5ab71505e3bc</Identifier>
        <Name>Code</Name>
        <ParameterSelections/>
        <ParameterSetCollections/>
        <Parameters/>
        <Summary i:nil="true"/>
        <Instance>25486d6c-36ba-4ab2-9fa6-0dbafbcf0389</Instance>
        <ConditionValue>
            <ComplexValue i:nil="true"/>
            <Text i:nil="true" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
            <Default>
                <ComplexValue i:nil="true"/>
                <Text xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
                    <b:string>NULLCODE</b:string>
                </Text>
            </Default>
        </ConditionValue>
        <TypeCode>String</TypeCode>
    </a:Condition>
    <a:Condition>
        <a:xmlns i:nil="true" xmlns:b="http://schema.test.org/2004/07/System.Xml.Serialize"/>
        <Identifier>0af860f6-5611-4a23-96dc-eb3863975529</Identifier>
        <Name>Content Type</Name>
        <ParameterSelections/>
        <ParameterSetCollections/>
        <Parameters/>
        <Summary i:nil="true"/>
        <Instance>6364ec20-306a-4cab-aabc-8ec65c0903c9</Instance>
        <ConditionValue>
            <ComplexValue i:nil="true"/>
            <Text i:nil="true" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
            <Default>
                <ComplexValue i:nil="true"/>
                <Text xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
                    <b:string>Standard</b:string>
                </Text>
            </Default>
        </ConditionValue>
        <TypeCode>String</TypeCode>
    </a:Condition>
</Conditions>

我的工作是交换其中一个值,保留源的整个结构,并使用它稍后在应用程序中提交 POST。

我遇到的问题是,当它保存到字符串或文件时,它会完全弄乱命名空间:

<ns0:Context xmlns:ns0="http://Test.the.Sdk/2010/07" xmlns:ns1="http://schema.test.org/2004/07/Test.Soa.Vocab" xmlns:ns3="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns1:xmlns xsi:nil="true" />
<ns0:Conditions>
<ns1:Condition>
<ns1:xmlns xsi:nil="true" />
<ns0:Identifier>a23aacaf-9b6b-424f-92bb-5ab71505e3bc</ns0:Identifier>
<ns0:Name>Code</ns0:Name>
<ns0:ParameterSelections />
<ns0:ParameterSetCollections />
<ns0:Parameters />
<ns0:Summary xsi:nil="true" />
<ns0:Instance>25486d6c-36ba-4ab2-9fa6-0dbafbcf0389</ns0:Instance>
<ns0:ConditionValue>
<ns0:ComplexValue xsi:nil="true" />
<ns0:Text xsi:nil="true" />
<ns0:Default>
<ns0:ComplexValue xsi:nil="true" />
<ns0:Text>
<ns3:string>NULLCODE</ns3:string>
</ns0:Text>
</ns0:Default>
</ns0:ConditionValue>
<ns0:TypeCode>String</ns0:TypeCode>
</ns1:Condition>
<ns1:Condition>
<ns1:xmlns xsi:nil="true" />
<ns0:Identifier>0af860f6-5611-4a23-96dc-eb3863975529</ns0:Identifier>
<ns0:Name>Content Type</ns0:Name>
<ns0:ParameterSelections />
<ns0:ParameterSetCollections />
<ns0:Parameters />
<ns0:Summary xsi:nil="true" />
<ns0:Instance>6364ec20-306a-4cab-aabc-8ec65c0903c9</ns0:Instance>
<ns0:ConditionValue>
<ns0:ComplexValue xsi:nil="true" />
<ns0:Text xsi:nil="true" />
<ns0:Default>
<ns0:ComplexValue xsi:nil="true" />
<ns0:Text>
<ns3:string>Standard</ns3:string>
</ns0:Text>
</ns0:Default>
</ns0:ConditionValue>
<ns0:TypeCode>String</ns0:TypeCode>
</ns1:Condition>
</ns0:Conditions>

我已将代码缩小到最基本的形式,但我仍然得到相同的结果,所以这与我正常操作文件的方式无关:

import xml.etree.ElementTree as ET
import requests

get_context_xml = 'http://localhost/testapi/returnxml' #returns first XML example above.
source_context_xml = requests.get(get_context_xml)

Tree = ET.fromstring(source_context_xml)

#Ensure the original namespaces are intact.
for Conditions in Tree.iter('{http://schema.test.org/2004/07/Test.Soa.Vocab}Condition'): 
    print "success"

with open('/home/memyself/output.xml','w') as f:
    f.write(ET.tostring(Tree))

【问题讨论】:

  • 您用“lxml”标记了问题。你试过了吗?我认为如果你这样做的话,大多数问题(如果不是全部的话)都会消失。 lxml 与 ElementTree 类似,但不考虑命名空间。

标签: python xml lxml elementtree


【解决方案1】:

在执行fromstring()(读取xml)之前,您需要register 前缀和命名空间以避免默认命名空间前缀(如ns0ns1 等)。

您可以为此使用ET.register_namespace() 函数,示例-

ET.register_namespace('<prefix>','http://Test.the.Sdk/2010/07')
ET.register_namespace('a','http://schema.test.org/2004/07/Test.Soa.Vocab')

如果您不想要前缀,可以将 &lt;prefix&gt; 留空。


示例/演示 -

>>> r = ET.fromstring('<a xmlns="blah">a</a>')
>>> ET.tostring(r)
b'<ns0:a xmlns:ns0="blah">a</ns0:a>'
>>> ET.register_namespace('','blah')
>>> r = ET.fromstring('<a xmlns="blah">a</a>')
>>> ET.tostring(r)
b'<a xmlns="blah">a</a>'

【讨论】:

  • 谢谢 我对为前缀设置什么值感到困惑。查看整个原始 XML 中的所有声明,我如何将哪个前缀关联到哪个命名空间? xmlns="http://Test.the.Sdk/2010/07" xmlns="http://schema.test.org/2004/07/Test.Soa.Vocab" xmlns:a="http://schema.test.org/2004/07/System.Xml.Serialize" xmlns:a="http://schema.test.org/2004/07/Test.Soa.Vocab" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:b="http://schema.test.org/2004/07/System.Xml.Serialize" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
  • :之后的前缀分配给命名空间,如果xmlns行中没有此项,则将前缀设置为空。示例 - b 用于 http://schemas.microsoft.com/2003/10/Serialization/Arraysb 用于 http://schema.test.org/2004/07/System.Xml.Serialize 。但是您也可以指定自己的前缀,这样更易​​读(源 xml 似乎对多个命名空间使用相同的前缀,虽然有效,但可能不利于可读性)。
  • 不幸的是,我无法将其保存为与打开时完全相同的格式。现在它添加了一个更大的前缀声明并保留了 ns0 没有办法让 ETree 保持其打开方式的格式?
  • 还是 ns0ns1 吗?而且您确实在读取 xml 之前添加了命名空间,对吗?正如建议的那样 - 在你做 fromstring() 之前(阅读 xml)
  • 正确,我脚本的第一行是:ET.register_namespace('', 'http://Telestream.Vantage.Sdk/2010/07') ET.register_namespace('i', 'http://www.w3.org/2001/XMLSchema-instance') ET.register_namespace('', 'http://schemas.datacontract.org/2004/07/Telestream.Soa.Vocabulary') ET.register_namespace('b', 'http://schemas.datacontract.org/2004/07/System.Xml.Serialization') ET.register_namespace('a', 'http://schemas.datacontract.org/2004/07/System.Xml.Serialization') ET.register_namespace('a', 'http://schemas.datacontract.org/2004/07/Telestream.Soa.Vocabulary')
【解决方案2】:

首先,welcome 加入 StackOverflow 网络!技术上@anand-s-kumar 是正确的。然而,toString 函数存在轻微误用,并且代码可能并不总是知道命名空间,或者标签或 XML 文件之间的命名空间并不相同。此外,lxmlxml.etree 库与 Python 2.x 和 3.x 之间的不一致使得处理这一问题变得困难。

此函数遍历传入的 XML 树tree 中的所有子元素,然后编辑 XML 标记以删除命名空间。请注意,这样做可能会丢失一些数据

def remove_namespaces(tree):
    for el in tree.getiterator():
        match = re.match("^(?:\{.*?\})?(.*)$", el.tag)
        if match:
            el.tag = match.group(1)

我自己也遇到了这个问题,并想出了一个快速的解决方案。我在大约 81,000 个存在此问题的 XML 文件(每个文件平均大约 150 MB)上对此进行了测试,并且所有这些文件都已修复。请注意,这并不是一个最佳解决方案,但它相对有效并且对我来说效果很好。

CREDIT:创意和代码结构最初来自Jochen Kupperschmidt

【讨论】:

  • 谢谢,非常有趣。我将通过 REST API 提交 POST,但我不确定接收节点是否会在没有命名空间的情况下接受它。如果它忽略它们,那将是理想的。我会看看我能掀起什么。谢谢。
猜你喜欢
  • 2020-10-14
  • 1970-01-01
  • 2011-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-02
  • 2012-01-25
相关资源
最近更新 更多