2015-08-04 6 views
5

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

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

То, что я пытаюсь выполнить, - это просто захватить этот XML, поменять малейшее значение (значение b: строковое значение user ConditionValue/Default, но это не имеет отношения к этому вопросу) , а затем сохранить его как строку для отправки позже в режиме ожидания 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)) 
+0

Вы отметили вопрос с помощью "lxml". Вы попробовали? Я думаю, что большинство, если не все проблемы исчезнут, если вы это сделаете. lxml похож на ElementTree, но оставляет только пространства имен. – mzjn

ответ

8

Вам нужно register префикс и пространство имен, прежде чем делать fromstring() (Чтение XML) в избегайте префиксов пространства имен по умолчанию (например, ns0 и ns1, и т.п.).

Вы можете использовать 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') 

Вы можете оставить <prefix> пустым, если вы не хотите, префикс.


Пример/Demo -

>>> 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>' 
+0

Thanks Я смущен тем, какие значения заданы для префиксов. Рассматривая все объявления во всем исходном 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" ' – emmdee

+0

Назначьте префикс после': 'для пространства имен, если в строке' xmlns' нет такого элемента, а затем установите префикс как пустой. Пример - 'b' для' http: // schemas.microsoft.com/2003/10/Serialization/Arrays' и 'b' для' http://schema.test.org/2004/07/System.Xml.Serialize . Но вы также можете указать свои собственные префиксы, которые более читабельны (исходный xml, похоже, использует один и тот же префикс для нескольких пространств имен, который хотя и действителен, может быть непригоден для читаемости). –

+0

К сожалению, я не могу получить его, чтобы сохранить его в том же формате, который он открыл. Теперь он добавил более крупное объявление префиксов и сохранил ns0 Невозможно заставить ETree просто сохранить форматирование так, как он был открыт? – emmdee

0

Во-первых, welcome к сети 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 МБ каждый), у которых была эта проблема, и все они были исправлены. Обратите внимание, что это не совсем оптимальное решение, но оно относительно эффективно и отлично работает для меня.

КРЕДИТ: Идея и кодовая структура первоначально от Jochen Kupperschmidt.

+0

Спасибо и очень интересно. Я собираюсь отправить POST через REST API, и я не уверен, что принимающий узел примет его без пространств имен. Это было бы идеально, если бы оно их игнорировало. Я посмотрю, что я могу взломать. Благодарю. – emmdee

Смежные вопросы