2015-08-04 6 views
5

Vedo che ci sono domande simili qui, ma nulla che mi abbia totalmente aiutato. Ho anche guardato la documentazione ufficiale sui namespace ma non riesco a trovare nulla che mi stia davvero aiutando, forse sono solo troppo nuovo per la formattazione XML. Capisco che forse ho bisogno di creare il mio dizionario del namespace? In entrambi i casi, ecco la mia situazione:Salvataggio XML tramite ETree in Python. Non mantiene gli spazi dei nomi e aggiunge ns0, ns1 e rimuove i tag xmlns

Sto ottenendo un risultato da una chiamata API, mi dà un XML che viene memorizzato come una stringa nella mia applicazione Python.

Quello che sto cercando di ottenere è catturare questo XML, scambiare un valore minuscolo (Il valore b: stringa valore ConditionValue/Default ma non è rilevante per questa domanda) e quindi salvarlo come stringa da inviare in seguito attivo in una chiamata POST Rest.

La sorgente XML assomiglia a questo:

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

Il mio lavoro è quello di scambiare uno dei valori, mantenendo l'intera struttura della sorgente, e utilizzare questo per inviare un post successivo su nell'applicazione.

Il problema che sto avendo è che quando si salva in una stringa o in un file, si scombina totalmente gli spazi dei nomi:

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

ho ristretto il codice fino alla forma più elementare e sto ancora ottenere gli stessi risultati, quindi non è nulla a che fare con il modo sto manipolando il file normalmente:

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

Hai taggato la domanda con "lxml". Hai provato? Penso che la maggior parte se non tutti i problemi andranno via se lo fai. lxml è simile a ElementTree, ma lascia da solo gli spazi dei nomi. – mzjn

risposta

8

è necessario register il prefisso e lo spazio dei nomi prima di fare fromstring() (lettura del xml) per evitare i prefissi dello spazio dei nomi predefinito (come ns0 e ns1, ecc.).

È possibile utilizzare la funzione ET.register_namespace() per questo, Esempio -

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

È possibile lasciare il vuoto <prefix> se non si desidera un prefisso.


Esempio/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

Grazie Sono confuso su quali valori impostare per i prefissi. Guardando tutte le dichiarazioni in tutto l'XML originale, come posso correlare quale prefisso assegnare a quale spazio dei nomi? '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

Assegnare il prefisso dopo': 'allo spazio dei nomi, se non esiste alcun elemento nella riga' xmlns', quindi impostare il prefisso come vuoto. Esempio - 'b' per' http: // schemas.microsoft.com/2003/10/Serialization/Arrays' e 'b' per' http://schema.test.org/2004/07/System.Xml.Serialize '. Ma puoi anche specificare i tuoi prefissi, che sono più leggibili (l'xml di origine sembra utilizzare lo stesso prefisso per più spazi dei nomi, che sebbene valido, potrebbe non essere buono per la leggibilità). –

+0

Purtroppo non riesco a farlo salvare nello stesso identico formato in cui è stato aperto. Ora ha aggiunto una dichiarazione più grande di prefissi e mantenuto il ns0 Non c'è modo di far sì che l'ETree mantenga la formattazione nel modo in cui è stata aperta? – emmdee

0

Prima di tutto, welcome alla rete StackOverflow! Tecnicamente @anand-s-kumar è corretto. Tuttavia, vi era un lieve uso improprio della funzione toString e il fatto che gli spazi dei nomi potrebbero non essere sempre conosciuti dal codice o lo stesso tra tag o file XML. Inoltre, le incoerenze tra le librerie lxml e e Python 2.xe 3.x rendono difficile la gestione.

Questa funzione scorre tutti gli elementi figli dell'albero XML tree che viene passato e quindi modifica i tag XML per rimuovere gli spazi dei nomi. Si noti che facendo ciò, alcuni dati potrebbero andare persi.

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

Io stesso mi sono imbattuto in questo problema e ho hackerato insieme una soluzione rapida. L'ho testato su circa 81.000 file XML (in media circa 150 MB ciascuno) che presentavano questo problema e tutti erano stati risolti. Si noti che questa non è esattamente una soluzione ottimale, ma è relativamente efficiente e ha funzionato abbastanza bene per me.

CREDITO: Idea e la struttura del codice originario di Jochen Kupperschmidt.

+0

Grazie e molto interessante. Ho intenzione di inviare un POST tramite un'API REST e non sono sicuro che il nodo ricevente lo accetti senza spazi dei nomi. Sarebbe l'ideale se li ignorasse. Vedrò cosa posso sferzare. Grazie. – emmdee

Problemi correlati