Сохранение XML с помощью ETree в Python. Это не сохранение пространств имен, а добавление NS0, ns1 и удаление тегов xmlns

Я вижу, что здесь есть подобные вопросы, но ничего, что полностью помогло мне. Я также просмотрел официальную документацию по пространствам имен, но не могу найти ничего, что действительно помогает мне, возможно, я просто слишком новичок в форматировании XML. Я понимаю, что, возможно, мне нужно создать свой собственный словарь имен? В любом случае, вот моя ситуация:

Я получаю результат от вызова API, он дает мне XML, который хранится в виде строки в моем приложении Python.

то, что я пытаюсь выполнить, - это просто захватить этот XML, поменять крошечное значение (B: string value user 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>

моя задача-поменять одно из значений, сохранив всю структуру источника, и использовать это для отправки сообщения позже в приложении.

проблема, с которой я сталкиваюсь, заключается в том, что когда он сохраняет строку или файл, он полностью испортит пространства имен:

<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))

2 ответов


вам нужно зарегистрироваться префикс и пространство имен перед вами fromstring() (чтение xml), чтобы избежать префиксов пространства имен по умолчанию (например,ns0 и ns1, etc.) .

можно использовать ET.register_namespace()


во-первых, добро пожаловать в сеть StackOverflow! Технически @anand-s-kumar является правильным. Однако было небольшое злоупотребление toString функция и тот факт, что пространства имен не всегда могут быть известны кодом или одинаковыми между тегами или XML-файлами. Кроме того, несоответствия между lxml и xml.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 МБ каждый), у которых была эта проблема, и все они были исправлены. Обратите внимание, что это не совсем оптимальное решение, но оно относительно эффективно и работает довольно хорошо для мне.

кредит: идея и структура кода первоначально из Йохен Kupperschmidt.