[Zope3-checkins] CVS: ZODB4/src/persistence - pxml.py:1.1.2.1

Jeremy Hylton jeremy@zope.com
Fri, 30 May 2003 18:59:22 -0400


Update of /cvs-repository/ZODB4/src/persistence
In directory cvs.zope.org:/tmp/cvs-serv24224

Added Files:
      Tag: jeremy-xml-play-branch
	pxml.py 
Log Message:
A snippet of code for persistent xml objects.


=== Added File ZODB4/src/persistence/pxml.py ===
"""Simple persistent XML objects.

Inspired by Aaron Swartz's xmltramp.py.
http://www.aaronsw.com/2002/xmltramp/
"""

from persistence import Persistent

from xml.sax import make_parser
from xml.sax.handler import ContentHandler, EntityResolver, feature_namespaces
from xml.sax.saxutils import XMLGenerator

from cStringIO import StringIO

class Element(Persistent):
    # The Handler class below manages the object's attributes directly
    # to avoid name clashes between Element's methods and the xml
    # content.

    def __init__(self, name, attrs):
        self._name = name
        if attrs:
            self._attrs = Attributes(attrs)
        else:
            self._attrs = None
        self._content = None
        self._children = None

    def __repr__(self):
        return "<%s element>" % self._name

    def __str__(self):
        return self._content

class Attributes(Persistent):

    def __init__(self, attrdict):
        self.__dict__.update(attrdict)

def toXml(elt):
    f = StringIO()
    gen = XMLGenerator(f)
    gen.startDocument()
    _genToXml(gen, elt)
    gen.endDocument()
    return f.getvalue()

def _genToXml(gen, elt):
    if elt._attrs:
        d = elt._attrs.__dict__
    else:
        d = {}
    gen.startElement(elt._name, d)
    gen.characters(elt._content)
    for child in elt._children or []:
        _genToXml(gen, child)
    gen.endElement(elt._name)


class Handler(ContentHandler, EntityResolver):

    def __init__(self):
        self.stack = []
        self.chars = []
        self.root = None

    def startElement(self, name, attrs):
        self.stack.append(Element(name, attrs._attrs))

    def endElement(self, name):
        elt = self.stack.pop()
        assert elt._name == name
        elt._content = "".join(self.chars).strip()
        self.chars = []
        self.linkToParent(elt)
        self.childAttrs(elt)

    def linkToParent(self, elt):
        if self.stack:
            parent = self.stack[-1]
            if parent._children is None:
                parent._children = []
            parent._children.append(elt)
        else:
            self.root = elt

    def childAttrs(self, elt):
        if not elt._children:
            return
        d = {}
        for child in elt._children:
            d.setdefault(child._name, []).append(child)
        for k, v in d.items():
            k = k.replace(":", "_")
            if len(v) == 1:
                setattr(elt, k, v[0])
            else:
                setattr(elt, k, v)

    def characters(self, buf):
        self.chars.append(buf)

def parse(s):
    f = StringIO(s)
    h = Handler()
    p = make_parser()
    p.setFeature(feature_namespaces, 0)
    p.setContentHandler(h)
    p.parse(f)
    return h.root