XML in PostgreSql Db->Error can't adapt type 'instance'

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Dittrich
User
Beiträge: 21
Registriert: Mittwoch 26. Juni 2013, 08:50

Hallo zusammen,
ich möchte Daten von einem XML File in eine PostgreSQL Db schreiben.
Dazu nutze ich DOM und psycopg2.
Das Auslesen der Daten funktioniert soweit (können z.B. in der Console ausgeben werden).
Das schreiben in eine Datenbank habe ich zuerst getestet das funktioniert auch. Nun habe ich versucht die beiden Codeschnipsel zusammenzupacken.
Hierbei erhalte ich beim ausführen den Fehler : Error can't adapt type 'instance'.
Wie ich bereits gelesen habe liegt das wohl daran das der Typ der Variable fehlt.
Wie kann ich dies in meinem Code berücksichtigen?

Code: Alles auswählen

import psycopg2  # @UnresolvedImport
import sys

if __name__ == '__main__':
    pass

from xml.dom.minidom import parse

con = None

try:
    con=psycopg2.connect(database="********", host="******", user="*******", password="******")
    cur = con.cursor() 
    dom = parse('C:\\Dokumente und Einstellungen\\d.dittrich\Desktop\\12300126637.xml') # parse an XML file by name
    supply=dom.getElementsByTagName('Supply')
    for node in supply:
        conf_deliverynumber=node.getAttribute('Deliverynumber')
        deliverynumber=node.getElementsByTagName('Deliverynumber')
        for a in deliverynumber:
            text=a.childNodes[0].nodeValue
            print text  
            print  
        pallet=dom.getElementsByTagName('Pallet')    
        for node in pallet:
            conf_palletnumber=node.getAttribute('Palletnumber')
            palletnumber=node.getElementsByTagName('Palletnumber')
            for a in palletnumber:
                text=a.childNodes[0].nodeValue
                print text
                print 
            product=dom.getElementsByTagName('Product')
            for node in product:
                serialnumber=node.getElementsByTagName('Serialnumber')
                articlenumber=node.getElementsByTagName('Articlenumber')
                articledescription=node.getElementsByTagName('Articledescription')
                cwmp=node.getElementsByTagName('CWMP')
                maca=node.getElementsByTagName('maca')
                macb=node.getElementsByTagName('macb')
                macdsl=node.getElementsByTagName('macdsl')
                macwlan=node.getElementsByTagName('macwlan')
                usbBoardMac=node.getElementsByTagName('usb_board_mac')
                usbRndisMac=node.getElementsByTagName('usb_rndis_mac')          
                for a in serialnumber:
                    text=a.childNodes[0].nodeValue
                    print text
                for a in articlenumber:
                    text=a.childNodes[0].nodeValue
                    print text            
                for a in articledescription:
                    text=a.childNodes[0].nodeValue
                    print text        
                for a in cwmp:
                    text=a.childNodes[0].nodeValue
                    print text        
                for a in maca:
                    text=a.childNodes[0].nodeValue
                    print text            
                for a in macb:
                    text=a.childNodes[0].nodeValue
                    print text   
                for a in macdsl:
                    text=a.childNodes[0].nodeValue
                    print text 
                for a in macwlan:
                    text=a.childNodes[0].nodeValue
                    print text 
                for a in usbBoardMac:
                    text=a.childNodes[0].nodeValue
                    print text
                for a in usbRndisMac:
                    text=a.childNodes[0].nodeValue
                    print text
                print
                cur.execute("INSERT INTO acs_lager(serialnumber,artikelbezeichnung,cwmp_mac,dsl_mac)VALUES(%s,%s,%s,%s)",(serialnumber,articlenumber,cwmp[7:19],macdsl))
                con.commit()
except psycopg2.DatabaseError, e:
    
    if con:
        con.rollback()
    
    print 'Error %s' % e    
    sys.exit(1)
    
    
finally:
    
    if con:
        con.close()
                
Vielen Dank schonmal vorab
Zuletzt geändert von Anonymous am Mittwoch 26. Juni 2013, 09:21, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codetags gesetzt.
BlackJack

@Dittrich: Der Typ kann nicht fehlen weil jeder Wert einen Typ hat. Es ist hier allerdings ein Typ der nicht zu den Datenbankspalten passt. Alleine der erste Wert `serialnumber` ist doch das Ergebnis von einem `getElementsByTagName()`, also ein `NodeList`-Objekt das 0 oder mehr `Node`-Objekte enthalten kann, die wiederum ganze XML-Teilbäume repräsentieren können. Was soll denn die Datenbank *damit* als Spaltenwert für wahrscheinlich eine Zahl oder Zeichenkette für eine Seriennummer anfangen? Du musst da schon den Wert ermitteln den Du tatsächlich in der Datenbankspalte haben möchtest und nicht hoffen das sich die DB-Schnittstelle aus einem haufen XML-Teilbäume schon auf magische Weise das heraussuchen wird, was Du am Ende gerne in der Datenbank hättest.

Statt DOM würde ich übrigens die ElementTree-API aus der Standardbibliothek verwenden. Das führt zu „pythonischerem” Code.

Wenn man die Verbindung vor dem ``try`` erstellt, dann muss man später nicht mit dem ``if con:`` prüfen ob es dieses Objekt überhaupt gibt.
Dittrich
User
Beiträge: 21
Registriert: Mittwoch 26. Juni 2013, 08:50

Ich hab das Problem gelöst und meinen Code etwas angepasst.

Code: Alles auswählen

import psycopg2  # @UnresolvedImport
import sys

if __name__ == '__main__':
    pass

from xml.dom.minidom import parse

con = None

try:
    con=psycopg2.connect(database="******", host="******", user="******", password="******")
    cur = con.cursor() 
    dom = parse('C:\\Dokumente und Einstellungen\\d.dittrich\Desktop\\12300126637.xml') # parse an XML file by name
    supply=dom.getElementsByTagName('Supply')
    for node in supply:
        conf_deliverynumber=node.getAttribute('Deliverynumber')
        deliverynumber=node.getElementsByTagName('Deliverynumber')
        for a in deliverynumber:
            text=a.childNodes[0].nodeValue
            print text  
            print  
        pallet=dom.getElementsByTagName('Pallet')    
        for node in pallet:
            conf_palletnumber=node.getAttribute('Palletnumber')
            palletnumber=node.getElementsByTagName('Palletnumber')
            for a in palletnumber:
                text=a.childNodes[0].nodeValue
                print text
                print 
            product=dom.getElementsByTagName('Product')
            for node in product:
                serialnumber=node.getElementsByTagName('Serialnumber')[0].childNodes[0].nodeValue
                articlenumber=node.getElementsByTagName('Articlenumber')[0].childNodes[0].nodeValue
                articledescription=node.getElementsByTagName('Articledescription')[0].childNodes[0].nodeValue
                cwmp=node.getElementsByTagName('CWMP')[0].childNodes[0].nodeValue
                maca=node.getElementsByTagName('maca')[0].childNodes[0].nodeValue
                macb=node.getElementsByTagName('macb')[0].childNodes[0].nodeValue
                macdsl=node.getElementsByTagName('macdsl')[0].childNodes[0].nodeValue
                macwlan=node.getElementsByTagName('macwlan')[0].childNodes[0].nodeValue
                usbBoardMac=node.getElementsByTagName('usb_board_mac')[0].childNodes[0].nodeValue
                usbRndisMac=node.getElementsByTagName('usb_rndis_mac') [0].childNodes[0].nodeValue         

                cur.execute("INSERT INTO acs_lager(serialnumber,artikelbezeichnung,cwmp_mac,dsl_mac)VALUES(%s,%s,%s,%s)",(serialnumber,articlenumber,cwmp[7:19],macdsl))
                
                con.commit()
except psycopg2.DatabaseError, e:
    
    if con:
        con.rollback()
    
    print 'Error %s' % e    
    sys.exit(1)
       
finally:
    
    if con:
        con.close() 
Danke @ BlackJack
Zuletzt geändert von Anonymous am Mittwoch 26. Juni 2013, 11:08, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@Dittrich: Da ist ein bisschen viel Kopieren und Einfügen für meinen Geschmack. Das kann man mit einer Funktion und einer Schleife sicher vermeiden. Und wie gesagt die Definition von `con` gehört vor das ``try``.
Dittrich
User
Beiträge: 21
Registriert: Mittwoch 26. Juni 2013, 08:50

@BlackJack
Und wie gesagt die Definition von `con` gehört vor das ``try``.

Code: Alles auswählen

con = None
con=psycopg2.connect(database="******", host="******", user="******", password="******")
      try:
Meinst du so?
Dann wird doch die Exception bei fehlgeschlagener Db Verbindung nicht ausgeführt oder seh ich das falsch
BlackJack

@Dittrich: Die erste Zuweisung mit `None` ist dann überflüssig. Wenn Du eine mögliche Ausnahme von dem `connect()` auch berücksichtigen willst, dann müsstest um das ganze ein ``try``/``except`` setzen und nach dem die Verbindung zugewiesen wurde ein ``try``/``finally`` für das „Aufräumen” der Verbindung verwenden. Oder die Verbindung mit ``with`` erstellen (eventuell mit `contextlib.closing()`).
Dittrich
User
Beiträge: 21
Registriert: Mittwoch 26. Juni 2013, 08:50

Ok das hab ich soweit verstanden. Eine Frage hätte ich aber noch^^
Wie kann ich in folgendem Code die Zeit und das Datum (2013-04-18 07:55:25) auslesen?

Code: Alles auswählen

 <Deliverynumber created="2013-04-18 07:55:25">XXXXXXXXXXXXXXX</Deliverynumber> 
Mfg
Dittrich
User
Beiträge: 21
Registriert: Mittwoch 26. Juni 2013, 08:50

Ich habe nun die komplette Zeile eingelesen.

Code: Alles auswählen

deliverynumber1 = node.getElementsByTagName('Deliverynumber')[0].toxml()
Gibt es hier auch eine bessere Möglichkeit?
BlackJack

@Dittrich: Da das nicht funktioniert weil es kein Datum erzeugt das man in der Form in die Datenbank eintragen kann, gibt es natürlich bessere Möglichkeiten. Welche die funktionieren halt. ;-)

Man muss das XML-Attribut `created` von dem Knoten abfragen, dann bekommt man eine Zeichenkette, die nur die Datumsangabe enthält. Eben den Wert von dem XML-Attribut.

Um das sicher in die Datenbank zu bekommen muss man diese Zeichenkette dann in die Bestandteile zerlegen, wie sie die `Timestamp()`-Funktion aus dem Datenbankmodul erwartet.

Erwähnte ich eigentlich schon das die ElementTree-API schöner ist als die DOM-API‽
Dittrich
User
Beiträge: 21
Registriert: Mittwoch 26. Juni 2013, 08:50

Erwähnte ich eigentlich schon das die ElementTree-API schöner ist als die DOM-API‽
Ja =)
Ich werde deine Lösung mal testen.
Meine läuft nun auch und sieht so aus:
deliverynumber1 ist nun delivertime

Code: Alles auswählen

delivertime = node.getElementsByTagName('Deliverynumber')[0].toxml()
cur.execute("INSERT INTO geraete_eingang(deliverynumber, palletnumber,delivertime)VALUES(%s,%s,%s)", (deliverynumber,palletnumber,delivertime[25:44])) 
Dieser Teil 2013-04-18 07:55:25 stimmt wohl mit der Db Syntax überein.

Mfg
BlackJack

@Dittrich: Das ist alles andere als robust und kann einem leicht um die Ohren fliegen. Zum einen das operieren auf XML als Zeichenkette, als auch die Annahme das die Teilzeichenkette dann ein Datum im Format der Datenbank enthält.
Antworten