#! /usr/bin/env python from ZSI import * """Polymorphic containers using TC.Choice Based on code and text from Dan Gunter <dkgunter@lbl.gov>. The TC.Choice typecode can be used to create a polymorphic type for a specified set of object types. Here's how: 1. Define all your data classes (D1, D2, ...). If they derive from a common base class (Base), some feel you get "cleaner" code. 2. Create a typecode class (TC_D1, TC_D2, ...) for each data class. 3. Create a "base" typecode that uses TC.Choice to do the actual parsing and serializing. Two versions are shown, below. Then you can instantiate, e.g., an Array that can handle multiple datatypes with: TC.Array("base-class-name", TC_Base(), "MyArray") """ class Base: pass class D1(Base): pass class D2(Base): pass class TC_D1(TC.TypeCode): pass class TC_D2(TC.TypeCode): pass D1.typecode = TC_D1() D2.typecode = TC_D2() # A simple version of TC_Base that is "hardwired" with the types of # objects it can handle. We defer setting the choice attribute because # with nested containers you could get too much recursion. class TC_Base(TC.TypeCode): def parse(self, elt, ps): return self.choice.parse(elt, ps)[1] def serialize(self, sw, pyobj, **kw): if not isinstance(pyobj, Base): raise TypeError(str(pyobj.__class__) + " not in type hierarchy") if isinstance(pyobj, D1): self.choice.serialize(sw, ('D1', pyobj), **kw) elif isinstance(pyobj, D2): self.choice.serialize(sw, ('D2', pyobj), **kw) else: raise TypeError(str(pyobj.__class__) + " unknown type") return def __getattr__(self, attr): if attr == 'choice': choice = TC.Choice((D1.typecode, D2.typecode), 'Item') self.__dict__['choice'] = choice return choice raise AttributeError(attr) ## Another version that takes a dictionary that maps element names to ## the python class. class TC_Polymorphic(TC.TypeCode): def __init__(self, name2class, pname=None, **kw): TC.TypeCode.__init__(self, pname, **kw) self.name2class = name2class def parse(self, elt, ps): return self.choice.parse(elt, ps)[1] def serialize(self, sw, pyobj, **kw): self.choice.serialize(sw, (self.class2name[pyobj.__class__], pyobj), **kw) def __getattr__(self, attr): if attr == 'choice': choice = TC.Choice( [getattr(v, 'typecode') for k,v in self.name2class.items()], 'Item') self.__dict__['choice'] = choice return choice if attr == 'class2name': class2name = {} for k,v in self.name2class.items(): class2name[v] = k self.__dict__['class2name'] = class2name return class2name raise AttributeError(attr) class P1: pass class P2: pass P1.typecode = TC.String('s') P2.typecode = TC.Integer('i') myTC = TC.Array("Base", TC_Polymorphic({'i': P2, 's': P1})) test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body xmlns='test-uri'> <array SOAP-ENC:arrayType="Base"> <i>34</i> <s>hello</s> <s>34</s> <i>12</i> <s>goodbye</s> </array> </SOAP-ENV:Body> </SOAP-ENV:Envelope>''' ps = ParsedSoap(test) a = myTC.parse(ps.body_root, ps) print a if 0: # XXX. Does not work. :( b = [ P1(), P1(), P2(), P1() ] b[0].s = 'string' b[1].s = '34' b[2].i = 34 b[3].s = 'adios' import sys sw = SoapWriter(sys.stdout) myTC.serialize(sw, b)