Seite 1 von 1

XML mit xml.sax in Python-Dateityp überführen

Verfasst: Dienstag 10. April 2007, 13:01
von alan
Hallo allerseits

ich habe mich mittlerweile etwas in Python eingearbeitet und will mich jetzt an einem etwas größeren Programm versuchen.

Ich will mit xml.sax eine XML-Datei lesen, das aus dictionaries und key-value-Paaren aufgebaut ist, z.B:

------

Code: Alles auswählen

<dict>
  <key>version</key><string>1.0.1</string>
  <key>...
  <dict>
    <key>1</key>
    <dict>
        <key>Name</key><string>alan</string>
    </dict>
    <key>2</key>
    <dict>
        <key>Name</key><string>huber</string>
    </dict>
  </dict>   
</dict>
-------

Um das in eine Python-Dateityp zu überführen, will ich das als Array repräsentieren, etwa so:
{'version':'1.0.1', {1:{'Name':'alan'}, 2:{'Name':'huber'}}}


Weiß jetzt allerdings nicht so recht weiter, wie ich das umsetzen soll:
Hier bin ich momentan:

Code: Alles auswählen

from xml.sax import make_parser
from xml.sax.handler import ContentHandler


class Reader(ContentHandler):

    def __init__(self):
        self._currentElement = None
        self._result = None

    def startElement(self, name, attrs):
        self.currentElement = name

    def endElement(self, name):
        pass

    def characters(self, content):
        self._handleValues(content)

    def _handleValues(self, value):
        doSomeMagic

Die einzigen Parameter, die _handleValues() hat, sind currentElement (z.B. 'string') und content (z.B. 'Name'), also nie Schlüssel und Wert ('Name' und 'alan') auf einmal, denn attrs.getValue[name] liefert hier ja Blödsinn.

Ich weiß jetzt nicht, wie ich weitermachen soll.

Ich bin für jede Hilfe sehr dankbar

Re: XML mit xml.sax in Python-Dateityp überführen

Verfasst: Dienstag 10. April 2007, 14:04
von Leonidas
Hallo alan, willkommen im Forum!
alan hat geschrieben:Ich weiß jetzt nicht, wie ich weitermachen soll.
Warum willst du unbedingt SAX verwenden? Von allen XML-APIs die Python unterstützt ist SAX die unangenehmste, die die low-level ist. Ist es Interesse an SAX oder könntest du dich mit DOM oder ElementTree ebenso anfrenden?

Verfasst: Dienstag 10. April 2007, 14:49
von BlackJack
Eine nicht ganz so hübsche, da quick'n'dirty geschriebene Lösung mit `iterparse()` aus `ElementTree`:

Code: Alles auswählen

from StringIO import StringIO
from elementtree import ElementTree as etree

source = """
<dict>
  <key>version</key><string>1.0.1</string>
  <key>spam</key>
  <dict>
    <key>1</key>
    <dict>
        <key>Name</key><string>alan</string>
    </dict>
    <key>2</key>
    <dict>
        <key>Name</key><string>huber</string>
    </dict>
  </dict>
</dict>
"""


def get_node(position, tag_name, nodes):
    real_position, node = nodes.next()
    if position != real_position or tag_name != node.tag:
        raise ValueError('expected %r %s tag but found %r %s tag instead'
                         % (tag_name, position, node.tag, real_position))
    return node


def parse_item(position, node, nodes):
    if (position, node.tag) != ('start', 'key'):
        raise ValueError("expected 'key' start tag but found %r %s instead"
                         % (node.tag, position))
    key = get_node('end', 'key', nodes).text
    position, node = nodes.next()
    if position != 'start' or node.tag not in ('string', 'dict'):
        raise ValueError("expected 'string' or 'dict' start tag but"
                         "found %r %s tag" % (node.tag, position))
    if node.tag == 'string':
        return (key, get_node('end', 'string', nodes).text)
    else:
        return (key, parse_dict(nodes))


def parse_dict(nodes):
    result = dict()
    while True:
        position, node = nodes.next()
        if (position, node.tag) == ('end', 'dict'):
            return result
        else:
            key, value = parse_item(position, node, nodes)
            result[key] = value


def parse(source_file):
    nodes = etree.iterparse(source_file, ('start', 'end'))
    get_node('start', 'dict', nodes)
    return parse_dict(nodes)


def main():
    print parse(StringIO(source))


if __name__ == '__main__':
    main()
Aus den Parse-Funktionen sollte man wahrscheinlich besser Methoden machen, damit man die `nodes` nicht herumreichen muss.

Verfasst: Dienstag 10. April 2007, 18:32
von alan
Ich habe [noch] relativ wenig Ahnung von XML..Was ich aus Tutorials mitgenommen habe, ist, dass SAX wesentlich performanter ist, als DOM. Von ElementTree lese ich allerdings gerade zum ersten Mal :)
Für SAX habe ich mich dann entschieden, weil meine XML-Dateien leicht um die 10 MB und mehr erreichen können. Ist da SAX nicht vernünftiger?

Verfasst: Dienstag 10. April 2007, 21:03
von BlackJack
Bei solchen Grössen kann SAX schon Sinn machen, weil nicht der ganze DOM-Baum im Speicher aufgebaut wird. Allerdings ist es etwas aufwändiger zu Programmieren.

Ich würde auf jeden Fall erst einmal testen, ob es mit realen grossen Dateien und `ElementTree` Probleme mit dem Speicherplatz gibt, bevor ich mich mit SAX herumschlage.