XML DOM AttributeError: 'NoneType' object has no attribute '

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

Hallo,

ich versuche mich gerade an xml und habe ein Beispiel aus den Python-Buch von Opengalileo genommen.

Generell verstanden habe ich, was das Programm macht.
Das einlesen von xml Datei klappt auch super. Allerdings wenn ich xml Dateien schreiben will, bekomme ich eine sehr "komische" Fehlermeldung.
Ich habe das Orakel Google bereits befragt, aber bin aus den Antworten da nicht so ganz schlau geworden.

Hier die Fehlermeldung:
Traceback (most recent call last):
File "...XMLGenerator/src/XMLGenerator.py", line 62, in <module>
schreibe_dict(eintraege,"ausgabe.xml")
File "...XMLGenerator/src/XMLGenerator.py", line 53, in schreibe_dict
tag_dict.appendChild(tag_eintrag)
File "/usr/lib/python2.6/xml/dom/minidom.py", line 121, in appendChild
_clear_id_cache(self)
File "/usr/lib/python2.6/xml/dom/minidom.py", line 1471, in _clear_id_cache
node.ownerDocument._id_cache.clear()
AttributeError: 'NoneType' object has no attribute '_id_cache'
Wie gesagt, mit der letzten Zeile:
AttributeError: 'NoneType' object has no attribute '_id_cache'
kann ich garnichts anfangen.

Hauptsächlich meckert er über die Zeile:

Code: Alles auswählen

 tag_dict.appendChild(tag_eintrag)
Hat jemand eine Idee oder einen Tipp?


Anbei noch der Code:

Code: Alles auswählen

import xml.dom.minidom as dom


def _knoten_auslesen(knoten): 
    return eval("%s('%s')" % (knoten.getAttribute("typ"), 
                              knoten.firstChild.data.strip()))

def lade_dict(dateiname): 
    d = {} 
    baum = dom.parse(dateiname)

    for eintrag in baum.firstChild.childNodes: 
        if eintrag.nodeName == "question": 
            schluessel = wert = None

            for knoten in eintrag.childNodes: 
                if knoten.nodeName == "string": 
                    schluessel = _knoten_auslesen(knoten) 
                elif knoten.nodeName == "query": 
                    wert = _knoten_auslesen(knoten)
                
            d[schluessel] = wert 
    return d

def _erstelle_eintrag(schluessel, wert): 
    tag_eintrag = dom.Element("question") 
    tag_schluessel = dom.Element("string") 
    tag_wert = dom.Element("query")

    tag_schluessel.setAttribute("typ", type(schluessel).__name__) 
    tag_wert.setAttribute("typ", type(wert).__name__)

    #hier muss ich dann die SPARQL Querie an den Server schicken und die Antworten hinzufuegen.
    text = dom.Text() 
    text.data = str(schluessel) 
    tag_schluessel.appendChild(text)

    text = dom.Text() 
    text.data = str(wert) 
    tag_wert.appendChild(text)

    tag_eintrag.appendChild(tag_schluessel) 
    tag_eintrag.appendChild(tag_wert) 
    return tag_eintrag

def schreibe_dict(d, dateiname): 
    baum = dom.Document() 
    tag_dict = dom.Element("musicbrainz-dataset") 
    baum.appendChild(tag_dict)

    for schluessel, wert in d.iteritems(): 
        tag_eintrag = _erstelle_eintrag(schluessel, wert) 
        tag_dict.appendChild(tag_eintrag)
        #tag_dict.appendChild(self,tag_eintrag)

    f = open(dateiname, "w") 
    baum.writexml(f, "", "\t", "\n") 
    f.close()


eintraege=lade_dict("test.xml")
schreibe_dict(eintraege,"ausgabe.xml")

Danke!
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Ich bin mit minidom nicht sonderlich vertraut (lxml ist halt schöner), aber ein Blick in die minidom-Dokumentation scheint anzudeuten, dass man Elemente explizit im Kontext des Dokuments erstellen muss. Ich kann mich hier aber natürlich täuschen da ich minidom schon vor Ewigkeiten zu den Akten gelegt habe.
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

ist denn das mit lxml noch einfach möglich? bzw besser möglich?

Dann würde ich mir nämlich in lxml mal anschauen, wie man damit xml Dateien auslesen und erzeugen kann.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ja. Alternativ kannst du auch `xml.etree.ElementTree` nehmen. Aber tu dir einen Gefallen und meide minidom.
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

Hey,

hab mir jetzt gerade mal ein Tutorial rausgesucht zu lxml,
mal schauen, wie ich damit zurecht komme.

Der Abend ist ja noch lang :-)
problembär

Sag' mal bitte bescheid, wenn Du zu lxml wechselst.
Ich nutze immer noch minidom und würde mir sonst Dein Problem mal anschauen. Hat natürlich keinen Zweck, wenn Du wechselst (was ok ist).
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

@problembär:
das wäre echt super.

hab mir jetzt mal lxml angeschaut, aber ich finde das längst nicht so intuitiv wie minidom.
Ich würde also lieber minidom weiter benutzten und nicht wchseln.

Aber wie gesagt, zu dem Fehler finde ich einfach nichts.
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

ich habe jetzt nochmal was anderes probiert:

Code: Alles auswählen

import xml.dom.minidom as dom
import xml.dom
 
# Dokument erzeugen
implement = xml.dom.getDOMImplementation()
doc = implement.createDocument(None, "musicbrainz-dataset", None)


def _knoten_auslesen(knoten): 
    return eval("%s('%s')" % (knoten.getAttribute("typ"), 
                              knoten.firstChild.data.strip()))

def lade_dict(dateiname): 
    d = {} 
    baum = dom.parse(dateiname)

    for eintrag in baum.firstChild.childNodes: 
        if eintrag.nodeName == "question": 
            schluessel = wert = None

            for knoten in eintrag.childNodes: 
                if knoten.nodeName == "string": 
                    schluessel = _knoten_auslesen(knoten) 
                elif knoten.nodeName == "query": 
                    wert = _knoten_auslesen(knoten)
                
            d[schluessel] = wert 
    return d

def schreibe_dict(d, dateiname): 
    for schluessel, wert in d.iteritems(): 
        #tag_dict.appendChild(self,tag_eintrag)
        questionElem = doc.createElement("Question")
        doc.appendChild(questionElem)
        nameElem = doc.createElement("String")
        questionElem.appendChild(nameElem)
        nameTextElem = doc.createTextNode(schluessel)
        nameElem.appendChild(nameTextElem)
        
        querryElem = doc.createElement("Query")
        questionElem.appendChild(querryElem)
        anschriftTextElem = doc.createTextNode(wert)
        querryElem.appendChild(anschriftTextElem)
        doc.documentElement.appendChild(querryElem)
    
    datei = open(dateiname, "w")
    doc.writexml(datei, "\n", "  ")
    datei.close()


eintraege=lade_dict("test.xml")
print(eintraege)
schreibe_dict(eintraege,"ausgabe.xml")
Allerdings bekomme ich jetzt als Fehlermeldung:
Traceback (most recent call last):
File ".../XMLGenerator/src/XMLGenerator.py", line 78, in <module>
schreibe_dict(eintraege,"ausgabe.xml")
File ".../XMLGenerator/src/XMLGenerator.py", line 56, in schreibe_dict
doc.appendChild(questionElem)
File "/usr/lib/python2.6/xml/dom/minidom.py", line 1552, in appendChild
"two document elements disallowed")
xml.dom.HierarchyRequestErr: two document elements disallowed
Ich vermute, dass ich irgendwie Root und Element vertausche, bzw die beiden einander überschneiden.

Aber ich sehe nicht wo, ihr zufällig?

Danke!
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

Fehler gefunden!

jetzt läuft alles.


So gehts richtig:

Code: Alles auswählen

import xml.dom.minidom as dom
import xml.dom
 
# Dokument erzeugen
implement = xml.dom.getDOMImplementation()
doc = implement.createDocument(None, "musicbrainz-dataset", None)


def _knoten_auslesen(knoten): 
    return eval("%s('%s')" % (knoten.getAttribute("typ"), 
                              knoten.firstChild.data.strip()))

def lade_dict(dateiname): 
    d = {} 
    baum = dom.parse(dateiname)

    for eintrag in baum.firstChild.childNodes: 
        if eintrag.nodeName == "question": 
            schluessel = wert = None

            for knoten in eintrag.childNodes: 
                if knoten.nodeName == "string": 
                    schluessel = _knoten_auslesen(knoten) 
                elif knoten.nodeName == "query": 
                    wert = _knoten_auslesen(knoten)
                
            d[schluessel] = wert 
    return d

def schreibe_dict(d, dateiname): 
    for schluessel, wert in d.iteritems(): 
        #tag_dict.appendChild(self,tag_eintrag)
        questionElem = doc.createElement("Question")
        #doc.appendChild(questionElem)
        nameElem = doc.createElement("String")
        questionElem.appendChild(nameElem)
        nameTextElem = doc.createTextNode(schluessel)
        nameElem.appendChild(nameTextElem)
        
        querryElem = doc.createElement("Query")
        questionElem.appendChild(querryElem)
        anschriftTextElem = doc.createTextNode(wert)
        querryElem.appendChild(anschriftTextElem)
        #questionElem.documentElement.appendChild(querryElem)
        
        doc.documentElement.appendChild(questionElem)
        
    datei = open(dateiname, "w")
    doc.writexml(datei, "\n", "  ")
    datei.close()


eintraege=lade_dict("test.xml")
print(eintraege)
schreibe_dict(eintraege,"ausgabe.xml")
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Jetzt solltest du nur noch ein wenig aufräumen.

doc brauchst du nicht auf Modulebene und kannst es daher in die Funktion schreibe_dict() verlagern.
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

oh,

ja, da hast du recht :-)


mache ich mal sofort!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Aufgabe an den geneigten Leser: den Code in 1/4 der Zeilen mit lxml implementieren :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

wie geht denn das mit lxml?

ich hab das nicht hinbekommen.
(Ich hab auch seid bestimmt 2 Jahren kein Python mehr angefasst)


Wobei das ja jetzt nur ein Teil des Programmes ist, dazu gehören noch einige ander selbs geschriebene Parser und noch Server Aufrufe etc... Ansonsten würde das Programm ja so jetzt nicht viel machen :-D
BlackJack

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import __builtin__
from itertools import imap
from lxml import etree
from lxml.etree import Element, ElementTree, SubElement


def load(filename):
    def convert_content(node):
        return __builtin__.get(node.get('typ'))(node.text.strip())
    
    def convert_node(node):
        return [convert_content(node.find(x)) for x in ['string', 'query']]
    
    return dict(imap(convert_node, etree.parse(filename).xpath('//question')))


def save(mapping, filename):
    root = Element('musicbrainz-dataset')
    for key, value in mapping.iteritems():
        question_node = SubElement(root, 'Question')
        SubElement(question_node, 'String').text = key
        SubElement(question_node, 'Query').text = value
    ElementTree(root, pretty_print=True).write(filename)


def main():
    mapping = load('test.xml')
    print mapping
    save(mapping, 'ausgabe.xml')


if __name__ == "__main__":
    main()
BlackMamba
User
Beiträge: 77
Registriert: Samstag 24. März 2007, 23:22
Wohnort: Germany,NRW,

das sieht natürlich einiges einfacher aus, aber das habe ich irgendwie nicht so hinbekommen.


Aber ich werde es mir nachher mal in Ruhe anschauen.
Aber ich habe gestern schon festgestellt, Python an sich ist sehr sehr angenehm, ich denke, damit werde ich öfters jetzt was machen.

Danke!!
Antworten