1 #---------------------------------------------------------------------------- 
   2 # Name:         xmlmarshaller.py 
   5 # Authors:      John Spurling, Joel Hare, Jeff Norton, Alan Mullendore 
   9 # Copyright:    (c) 2004-2005 ActiveGrid, Inc. 
  10 # License:      wxWindows License 
  11 #---------------------------------------------------------------------------- 
  14 from activegrid
.util
.lang 
import * 
  18 import xml
.sax
.handler
 
  19 import xml
.sax
.saxutils
 
  22 import activegrid
.util
.utillang 
as utillang
 
  23 import activegrid
.util
.objutils 
as objutils
 
  24 import activegrid
.util
.sysutils 
as sysutils
 
  25 import activegrid
.util
.aglogging 
as aglogging
 
  27 MODULE_PATH 
= "__main__" 
  29 ## ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed 
  30 ##unboundedVal = 2147483647 # value used for maxOccurs == "unbounded" 
  33 Special attributes that we recognize: 
  37 description: the name of the xml element for the marshalled object 
  39 name: __xmlattributes__ 
  41 description: the name(s) of the Lang string attribute(s) to be 
  42 marshalled as xml attributes instead of nested xml elements. currently 
  43 these can only be strings since there"s not a way to get the type 
  44 information back when unmarshalling. 
  48 description: the name(s) of the lang attribute(s) to skip when 
  53 description: describes an alternate Lang <-> XML name mapping.   
  54 Normally the name mapping is the identity function.  __xmlrename__ 
  55 overrides that.  The keys are the Lang names, the values are their 
  58 name: __xmlflattensequence__ 
  59 type: dict, tuple, or list 
  60 description: the name(s) of the Lang sequence attribute(s) whose 
  61 items are to be marshalled as a series of xml elements (with an 
  62 optional keyword argument that specifies the element name to use) as 
  63 opposed to containing them in a separate sequence element, e.g.: 
  66 <!-- normal way of marshalling --> 
  68   <item objtype="int">1</item> 
  69   <item objtype="int">2</item> 
  71 <!-- with __xmlflattensequence__ set to {"myseq": "squish"} --> 
  72 <squish objtype="int">1</squish> 
  73 <squish objtype="int">2</squish> 
  75 name: __xmlnamespaces__ 
  77 description: a dict of the namespaces that the object uses.  Each item 
  78 in the dict should consist of a prefix,url combination where the key is 
  79 the prefix and url is the value, e.g.: 
  81 __xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" } 
  83 name: __xmldefaultnamespace__ 
  85 description: the prefix of a namespace defined in __xmlnamespaces__ that 
  86 should be used as the default namespace for the object. 
  88 name: __xmlattrnamespaces__ 
  90 description: a dict assigning the Lang object"s attributes to the namespaces 
  91 defined in __xmlnamespaces__.  Each item in the dict should consist of a 
  92 prefix,attributeList combination where the key is the prefix and the value is 
  93 a list of the Lang attribute names.  e.g.: 
  95 __xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] } 
  97 name: __xmlattrgroups__ 
  99 description: a dict specifying groups of attributes to be wrapped in an enclosing tag. 
 100 The key is the name of the enclosing tag; the value is a list of attributes to include 
 103 __xmlattrgroups__ = {"name": ["firstName", "lastName"], "address": ["addressLine1", "city", "state", "zip"]} 
 105 name: __xmlcdatacontent__ 
 107 description: value is the name of a string attribute that should be assigned CDATA content from the 
 108 source document and that should be marshalled as CDATA. 
 110 __xmlcdatacontent__ = "messyContent" 
 114 global xmlMarshallerLogger
 
 115 xmlMarshallerLogger 
= logging
.getLogger("activegrid.util.xmlmarshaller.marshal") 
 116 # INFO  : low-level info 
 117 # DEBUG : debugging info 
 119 ################################################################################ 
 123 ################################################################################ 
 125 class Error(Exception): 
 126     """Base class for errors in this module.""" 
 129 class UnhandledTypeException(Error
): 
 130     """Exception raised when attempting to marshal an unsupported 
 133     def __init__(self
, typename
): 
 134         self
.typename 
= typename
 
 136         return "%s is not supported for marshalling." % str(self
.typename
) 
 138 class XMLAttributeIsNotStringType(Error
): 
 139     """Exception raised when an object"s attribute is specified to be 
 140     marshalled as an XML attribute of the enclosing object instead of 
 143     def __init__(self
, attrname
, typename
): 
 144         self
.attrname 
= attrname
 
 145         self
.typename 
= typename
 
 147         return """%s was set to be marshalled as an XML attribute 
 148         instead of a nested element, but the object"s type is %s, not 
 149         string.""" % (self
.attrname
, self
.typename
) 
 151 class MarshallerException(Exception): 
 154 class UnmarshallerException(Exception): 
 157 ################################################################################ 
 161 ################################################################################ 
 164 XMLNS_PREFIX 
= XMLNS 
+ ":" 
 165 XMLNS_PREFIX_LENGTH 
= len(XMLNS_PREFIX
) 
 166 DEFAULT_NAMESPACE_KEY 
= "__DEFAULTNS__" 
 168 XMLSCHEMA_XSD_URL 
= "http://www.w3.org/2001/XMLSchema" 
 169 AG_URL 
= "http://www.activegrid.com/ag.xsd" 
 171 BASETYPE_ELEMENT_NAME 
= "item" 
 172 DICT_ITEM_NAME 
= "qqDictItem" 
 173 DICT_ITEM_KEY_NAME 
= "key" 
 174 DICT_ITEM_VALUE_NAME 
= "value" 
 176 # This list doesn"t seem to be used. 
 177 #   Internal documentation or useless? You make the call! 
 178 ##MEMBERS_TO_SKIP = ("__module__", "__doc__", "__xmlname__", "__xmlattributes__", 
 179 ##                   "__xmlexclude__", "__xmlflattensequence__", "__xmlnamespaces__", 
 180 ##                   "__xmldefaultnamespace__", "__xmlattrnamespaces__", 
 181 ##                   "__xmlattrgroups__") 
 183 ################################################################################ 
 185 # classes and functions 
 187 ################################################################################ 
 189 def setattrignorecase(object, name
, value
): 
 190 ##    print "[setattrignorecase] name = %s, value = %s" % (name, value) 
 191     if (name 
not in object.__dict
__): 
 192         namelow 
= name
.lower() 
 193         for attr 
in object.__dict
__: 
 194             if attr
.lower() == namelow
: 
 195                 object.__dict
__[attr
] = value
 
 197     object.__dict
__[name
] = value
 
 199 def getComplexType(obj
): 
 200     if (hasattr(obj
, "_instancexsdcomplextype")): 
 201         return obj
._instancexsdcomplextype
 
 202     if (hasattr(obj
, "__xsdcomplextype__")): 
 203         return obj
.__xsdcomplextype
__ 
 206 def _objectfactory(objtype
, objargs
=None, objclass
=None): 
 207     "dynamically create an object based on the objtype and return it." 
 208     if not isinstance(objargs
, list): 
 210     if (objclass 
!= None): 
 212         if (len(objargs
) > 0): 
 213             if (hasattr(objclass
, "__xmlcdatacontent__")): 
 215                 contentAttr 
= obj
.__xmlcdatacontent
__ 
 216                 obj
.__dict
__[contentAttr
] = str(objargs
[0]) 
 218                 obj 
= objclass(*objargs
) 
 221         if ((obj 
!= None) and (hasattr(obj
, 'postUnmarshal'))): 
 224     return objutils
.newInstance(objtype
, objargs
) 
 226 class GenericXMLObject(object): 
 227     def __init__(self
, content
=None): 
 229             self
._content 
= content
 
 230             self
.__xmlcontent
__ = '_content' 
 233         return "GenericXMLObject(%s)" % objutils
.toDiffableString(self
.__dict
__) 
 235     def setXMLAttributes(self
, xmlName
, attrs
=None, children
=None, nsMap
=None, defaultNS
=None): 
 237             i 
= xmlName
.rfind(':') 
 239                 self
.__xmlname
__ = xmlName
 
 240                 if defaultNS 
!= None: 
 241                     self
.__xmldefaultnamespace
__ = str(defaultNS
) 
 243                 self
.__xmlname
__ = xmlName
[i
+1:] 
 245                 if nsMap
.has_key(prefix
): 
 246                     self
.__xmldefaultnamespace
__ = str(nsMap
[prefix
]) 
 248             for attrname
, attr 
in attrs
.items(): 
 249                 attrname 
= str(attrname
) 
 250                 if attrname 
== XMLNS 
or attrname
.startswith(XMLNS_PREFIX
): 
 252                 elif attrname 
== "objtype": 
 255                     if not hasattr(self
, '__xmlattributes__'): 
 256                         self
.__xmlattributes
__ = [] 
 257                     i 
= attrname
.rfind(':') 
 259                         prefix 
= attrname
[:i
] 
 260                         attrname 
= attrname
[i
+1:] 
 261                         if not hasattr(self
, '__xmlattrnamespaces__'): 
 262                             self
.__xmlattrnamespaces
__ = {} 
 263                         if self
.__xmlattrnamespaces
__.has_key(prefix
): 
 264                             alist 
= self
.__xmlattrnamespaces
__[prefix
] 
 267                         alist
.append(attrname
) 
 268                         self
.__xmlattrnamespaces
__[prefix
] = alist
 
 269                     self
.__xmlattributes
__.append(attrname
) 
 270             if hasattr(self
, '__xmlattributes__'): 
 271                 self
.__xmlattributes
__.sort() 
 272         if children 
!= None and len(children
) > 0: 
 275             for childname
, child 
in children
: 
 276                 childstr 
= str(childname
) 
 277                 if childstr 
in childList
: 
 278                     if not flattenList
.has_key(childstr
): 
 279                         flattenList
[childstr
] = (childstr
,) 
 281                     childList
.append(childstr
) 
 282             if len(flattenList
) > 0: 
 283                 self
.__xmlflattensequence
__ = flattenList
 
 285     def initialize(self
, arg1
=None): 
 290     def __init__(self
, name
, attrs
=None, xsname
=None): 
 299     def getobjtype(self
): 
 300 #        objtype = self.attrs.get("objtype") 
 301         objtype 
= self
.objtype
 
 302         if (objtype 
== None): 
 303             if (len(self
.children
) > 0): 
 309 class NsElement(object): 
 313         self
.defaultNS 
= None 
 317         if self
.prefix 
== None: 
 318             strVal 
= 'prefix = None; ' 
 320             strVal 
= 'prefix = "%s"; ' % (self
.prefix
) 
 321         if self
.targetNS 
== None: 
 322             strVal 
+= 'targetNS = None; ' 
 324             strVal 
+= 'targetNS = "%s"; ' % (self
.targetNS
) 
 325         if self
.defaultNS 
== None: 
 326             strVal 
+= 'defaultNS = None; ' 
 328             strVal 
+= 'defaultNS = "%s"; ' % (self
.defaultNS
) 
 329         if len(self
.nsMap
) == 0: 
 330             strVal 
+= 'nsMap = None; ' 
 332             strVal 
+= 'nsMap = {' 
 333             for ik
, iv 
in self
.nsMap
.iteritems(): 
 334                 strVal 
+= '%s=%s; ' % (ik
,iv
) 
 338     def setKnownTypes(self
, masterKnownTypes
, masterKnownNamespaces
, parentNSE
): 
 339         # if we're a nested element, extend our parent element's mapping 
 340         if parentNSE 
!= None: 
 341             self
.knownTypes 
= parentNSE
.knownTypes
.copy() 
 342             # but if we have a different default namespace, replace the parent's default mappings 
 343             if (self
.defaultNS 
!= None) and (parentNSE
.defaultNS 
!= self
.defaultNS
): 
 344                 newKT 
= self
.knownTypes
.copy() 
 346                     if tag
.find(':') < 0: 
 347                         del self
.knownTypes
[tag
] 
 348             newMap 
= parentNSE
.nsMap
.copy() 
 350                 for k
, v 
in self
.nsMap
.iteritems(): 
 356         # TODO: instead of starting with the knownNamespaces, start with the "xmlms" mappings 
 357         # for this element. Then we'd only process the namespaces and tags we need to. 
 358         # But for now, this works. 
 359         for long, short 
in masterKnownNamespaces
.iteritems(): 
 360             reversedKNS
[short
] = long 
 361         mapLongs 
= self
.nsMap
.values() 
 362         for tag
, mapClass 
in masterKnownTypes
.iteritems(): 
 364             if i 
>= 0:                                      # e.g. "wsdl:description" 
 365                 knownTagShort 
= tag
[:i
]                     # "wsdl" 
 366                 knownTagName 
= tag
[i
+1:]                    # "description" 
 367                 knownTagLong  
= reversedKNS
[knownTagShort
]  # e.g. "http://schemas.xmlsoap.org/wsdl" 
 368                 if (knownTagLong 
in mapLongs
): 
 369                     for mShort
, mLong 
in self
.nsMap
.iteritems(): 
 370                         if mLong 
== knownTagLong
: 
 371                             actualShort 
= mShort  
# e.g. "ws" 
 372                             actualTag 
= '%s:%s' % (actualShort
, knownTagName
) 
 373                             self
.knownTypes
[actualTag
] = mapClass
 
 375                 if self
.defaultNS 
== knownTagLong
: 
 376                     self
.knownTypes
[knownTagName
] = mapClass
 
 377             else:                                           # e.g. "ItemSearchRequest" 
 378                 self
.knownTypes
[tag
] = mapClass
 
 380     def expandQName(self
, eName
, attrName
, attrValue
): 
 382         i 
= attrValue
.rfind(':') 
 384             if self
.defaultNS 
!= None: 
 385                 bigValue 
= '%s:%s' % (self
.defaultNS
, attrValue
) 
 387             attrNS 
= attrValue
[:i
] 
 388             attrNCName 
= attrValue
[i
+1:] 
 389             for shortNs
, longNs 
in self
.nsMap
.iteritems(): 
 390                 if shortNs 
== attrNS
: 
 391                     bigValue 
= '%s:%s' % (longNs
, attrNCName
) 
 395 class XMLObjectFactory(xml
.sax
.ContentHandler
): 
 396     def __init__(self
, knownTypes
=None, knownNamespaces
=None, xmlSource
=None, createGenerics
=False): 
 397         self
.rootelement 
= None 
 398         if xmlSource 
== None: 
 399             self
.xmlSource 
= "unknown" 
 401             self
.xmlSource 
= xmlSource
 
 402         self
.createGenerics 
= createGenerics
 
 404         self
.elementstack 
= [] 
 406         self
.collectContent 
= None 
 407         if (knownNamespaces 
== None): 
 408             self
.knownNamespaces 
= {} 
 410             self
.knownNamespaces 
= knownNamespaces
 
 411         self
.reversedNamespaces 
= {} 
 412         for longns
, shortns 
in self
.knownNamespaces
.iteritems(): 
 413             self
.reversedNamespaces
[shortns
] = longns
 
 415         if (knownTypes 
!= None): 
 416             for tag
, cls 
in knownTypes
.iteritems(): 
 421                     if not self
.reversedNamespaces
.has_key(shortns
): 
 422                         errorString 
= 'Error unmarshalling XML document from source "%s": knownTypes specifies an unmapped short namespace "%s" for element "%s"' % (self
.xmlSource
, shortns
, tag
) 
 423                         raise UnmarshallerException(errorString
) 
 424                     longns 
= self
.reversedNamespaces
[shortns
] 
 425                     tag 
= '%s:%s' % (longns
, tag
) 
 426                 self
.knownTypes
[tag
] = cls
 
 427         #printKnownTypes(self.knownTypes, 'Unmarshaller.XMLObjectFactory.__init__') 
 428         xml
.sax
.handler
.ContentHandler
.__init
__(self
) 
 430     def appendElementStack(self
, newElement
, newNS
): 
 431         self
.elementstack
.append(newElement
) 
 432         if (len(self
.nsstack
) > 0): 
 433             oldNS 
= self
.nsstack
[-1] 
 434             if newNS
.defaultNS 
== None: 
 435                 newNS
.defaultNS 
= oldNS
.defaultNS
 
 436             if newNS
.targetNS 
== None: 
 437                 newNS
.targetNS 
= oldNS
.targetNS
 
 438             if len(newNS
.nsMap
) == 0: 
 439                 newNS
.nsMap 
= oldNS
.nsMap
 
 440             elif len(oldNS
.nsMap
) > 0: 
 441                 map = oldNS
.nsMap
.copy() 
 442                 map.update(newNS
.nsMap
) 
 444         self
.nsstack
.append(newNS
) 
 447     def popElementStack(self
): 
 448         element 
= self
.elementstack
.pop() 
 449         nse 
= self
.nsstack
.pop() 
 452     ## ContentHandler methods 
 453     def startElement(self
, name
, attrs
): 
 454 ##        print '[startElement] <%s>' % (name) 
 455         if name 
== 'xs:annotation' or name 
== 'xsd:annotation': # should use namespace mapping here 
 457             self
.appendElementStack(Element(name
, attrs
.copy()), NsElement()) 
 460         if self
.collectContent 
!= None: 
 461             strVal 
= '<%s' % (name
) 
 462             for aKey
, aVal 
in attrs
.items(): 
 463                 strVal 
+= (' %s="%s"' % (aKey
, aVal
)) 
 465             self
.collectContent
.content 
+= strVal
 
 473         element 
= Element(name
, attrs
.copy(), xsname
=xsname
) 
 474         # if the element has namespace attributes, process them and add them to our stack 
 477         for k 
in attrs
.getNames(): 
 478             if k
.startswith('xmlns'): 
 480                 eLongNs 
= longNs 
+ '/' 
 481                 if str(eLongNs
) in asDict(self
.knownNamespaces
): 
 484                     nse
.defaultNS 
= longNs
 
 487                     nse
.nsMap
[shortNs
] = longNs
 
 488             elif k 
== 'targetNamespace': 
 489                 nse
.targetNS 
= attrs
.getValue(k
) 
 491                 objtype 
= attrs
.getValue(k
) 
 492         nse 
= self
.appendElementStack(element
, nse
) 
 494             if nse
.nsMap
.has_key(nsname
): 
 495                 longname 
= '%s:%s' % (nse
.nsMap
[nsname
], name
) 
 496 ##            elif objtype == None: 
 497 ##                errorString = 'Error unmarshalling XML document from source "%s": tag "%s" at line "%d", column "%d" has an undefined namespace' % (self.xmlSource, xsname, self._locator.getLineNumber(), self._locator.getColumnNumber()) 
 498 ##                raise UnmarshallerException(errorString) 
 499             elif self
.reversedNamespaces
.has_key(nsname
): 
 500                 longname 
= '%s:%s' % (self
.reversedNamespaces
[nsname
], name
) 
 503         elif nse
.defaultNS 
!= None: 
 504             longname 
= '%s:%s' % (nse
.defaultNS
, name
) 
 507         element
.objtype 
= objtype
 
 508         element
.objclass 
= self
.knownTypes
.get(longname
) 
 509         if element
.objclass 
== None and len(self
.knownNamespaces
) == 0: 
 510             # handles common case where tags are unqualified and knownTypes are too, but there's a defaultNS 
 511             element
.objclass 
= self
.knownTypes
.get(name
) 
 512         if (hasattr(element
.objclass
, "__xmlcontent__")): 
 513             self
.collectContent 
= element
 
 515     def characters(self
, content
): 
 516 ##        print '[characters] "%s" (%s)' % (content, type(content)) 
 517         if (content 
!= None): 
 518             if self
.collectContent 
!= None: 
 519                 self
.collectContent
.content 
+= content
 
 521                 self
.elementstack
[-1].content 
+= content
 
 523     def endElement(self
, name
): 
 524 ##        print "[endElement] </%s>" % name 
 527         if i 
>= 0:  # Strip namespace prefixes for now until actually looking them up in xsd 
 530             if xsname 
== "xs:annotation" or xsname 
== "xsd:annotation":     # here too 
 532                 self
.popElementStack() 
 534         if self
.collectContent 
!= None: 
 535             if xsname 
!= self
.collectContent
.xsname
: 
 536                 self
.collectContent
.content 
+= ('</%s>' % (xsname
)) 
 537                 self
.popElementStack() 
 540                 self
.collectContent 
= None 
 541         oldChildren 
= self
.elementstack
[-1].children
 
 542         element
, nse 
= self
.popElementStack() 
 543         if ((len(self
.elementstack
) > 1) and (self
.elementstack
[-1].getobjtype() == "None")): 
 544             parentElement 
= self
.elementstack
[-2] 
 545         elif (len(self
.elementstack
) > 0): 
 546             parentElement 
= self
.elementstack
[-1] 
 547         objtype 
= element
.getobjtype() 
 548         if (objtype 
== "None"): 
 550         constructorarglist 
= [] 
 551         if (len(element
.content
) > 0): 
 552             strippedElementContent 
= element
.content
.strip() 
 553             if (len(strippedElementContent
) > 0): 
 554                 constructorarglist
.append(element
.content
) 
 555         # If the element requires an object, but none is known, use the GenericXMLObject class 
 556         if ((element
.objclass 
== None) and (element
.attrs
.get("objtype") == None) and ((len(element
.attrs
) > 0) or (len(element
.children
) > 0))): 
 557             if self
.createGenerics
: 
 558                 element
.objclass 
= GenericXMLObject
 
 559         obj 
= _objectfactory(objtype
, constructorarglist
, element
.objclass
) 
 560         if element
.objclass 
== GenericXMLObject
: 
 561             obj
.setXMLAttributes(str(xsname
), element
.attrs
, element
.children
, nse
.nsMap
, nse
.defaultNS
) 
 562         complexType 
= getComplexType(obj
) 
 564             if (hasattr(obj
, "__xmlname__") and getattr(obj
, "__xmlname__") == "sequence"): 
 565                 self
.elementstack
[-1].children 
= oldChildren
 
 567         if (len(element
.attrs
) > 0) and not isinstance(obj
, list): 
 568             for attrname
, attr 
in element
.attrs
.items(): 
 569                 if attrname 
== XMLNS 
or attrname
.startswith(XMLNS_PREFIX
): 
 570                     if attrname
.startswith(XMLNS_PREFIX
): 
 571                         ns 
= attrname
[XMLNS_PREFIX_LENGTH
:] 
 574                     if complexType 
!= None or element
.objclass 
== GenericXMLObject
: 
 575                         if not hasattr(obj
, "__xmlnamespaces__"): 
 576                             obj
.__xmlnamespaces
__ = {ns:attr}
 
 577                         elif ns 
not in obj
.__xmlnamespaces
__: 
 578                             if (hasattr(obj
.__class
__, "__xmlnamespaces__")  
 579                                 and (obj
.__xmlnamespaces
__ is obj
.__class
__.__xmlnamespaces
__)): 
 580                                 obj
.__xmlnamespaces
__ = dict(obj
.__xmlnamespaces
__) 
 581                             obj
.__xmlnamespaces
__[ns
] = attr
 
 582                 elif not attrname 
== "objtype": 
 583                     if attrname
.find(":") > -1:  # Strip namespace prefixes for now until actually looking them up in xsd 
 584                         attrname 
= attrname
[attrname
.find(":") + 1:] 
 585                     if (complexType 
!= None): 
 586                         xsdElement 
= complexType
.findElement(attrname
) 
 587                         if (xsdElement 
!= None): 
 588                             type = xsdElement
.type 
 590                                 if (type == TYPE_QNAME
): 
 591                                     attr 
= nse
.expandQName(name
, attrname
, attr
) 
 592                                 type = xsdToLangType(type) 
 593                                 ### ToDO remove maxOccurs hack after bug 177 is fixed 
 594                                 if attrname 
== "maxOccurs" and attr 
== "unbounded": 
 597                                     attr 
= _objectfactory(type, attr
) 
 598                                 except Exception, exceptData
: 
 599                                     errorString 
= 'Error unmarshalling attribute "%s" at line %d, column %d in XML document from source "%s": %s' % (attrname
, self
._locator
.getLineNumber(), self
._locator
.getColumnNumber(), self
.xmlSource
, str(exceptData
)) 
 600                                     raise UnmarshallerException(errorString
) 
 602                         setattrignorecase(obj
, _toAttrName(obj
, attrname
), attr
) 
 603                     except AttributeError: 
 604                         errorString 
= 'Error setting value of attribute "%s" at line %d, column %d in XML document from source "%s": object type of XML element "%s" is not specified or known' % (attrname
, self
._locator
.getLineNumber(), self
._locator
.getColumnNumber(), self
.xmlSource
, name
) 
 605                         raise UnmarshallerException(errorString
) 
 606 ##                    obj.__dict__[_toAttrName(obj, attrname)] = attr 
 607         # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__ 
 609         if hasattr(obj
, "__xmlflattensequence__"): 
 610             flatten 
= obj
.__xmlflattensequence
__ 
 611             if (isinstance(flatten
, dict)): 
 612                 for sequencename
, xmlnametuple 
in flatten
.items(): 
 613                     if (xmlnametuple 
== None): 
 614                         flattenDict
[sequencename
] = sequencename
 
 615                     elif (not isinstance(xmlnametuple
, (tuple, list))): 
 616                         flattenDict
[str(xmlnametuple
)] = sequencename
 
 618                         for xmlname 
in xmlnametuple
: 
 619                             flattenDict
[xmlname
] = sequencename
 
 621                 raise Exception("Invalid type for __xmlflattensequence___ : it must be a dict") 
 623         # reattach an object"s attributes to it 
 624         for childname
, child 
in element
.children
: 
 625             if (childname 
in flattenDict
): 
 626                 sequencename 
= _toAttrName(obj
, flattenDict
[childname
]) 
 627                 if (not hasattr(obj
, sequencename
)): 
 628                     obj
.__dict
__[sequencename
] = [] 
 629                 sequencevalue 
= getattr(obj
, sequencename
) 
 630                 if (sequencevalue 
== None): 
 631                     obj
.__dict
__[sequencename
] = [] 
 632                     sequencevalue 
= getattr(obj
, sequencename
) 
 633                 sequencevalue
.append(child
) 
 634             elif (objtype 
== "list"): 
 636             elif isinstance(obj
, dict): 
 637                 if (childname 
== DICT_ITEM_NAME
): 
 638                     obj
[child
[DICT_ITEM_KEY_NAME
]] = child
[DICT_ITEM_VALUE_NAME
] 
 640                     obj
[childname
] = child
 
 642                 # don't replace a good attribute value with a bad one 
 643                 childAttrName 
= _toAttrName(obj
, childname
) 
 644                 if (not hasattr(obj
, childAttrName
)) or (getattr(obj
, childAttrName
) == None) or (getattr(obj
, childAttrName
) == []) or (not isinstance(child
, GenericXMLObject
)): 
 646                         setattrignorecase(obj
, childAttrName
, child
) 
 647                     except AttributeError: 
 648                         raise MarshallerException("Error unmarshalling child element \"%s\" of XML element \"%s\": object type not specified or known" % (childname
, name
)) 
 650         if (complexType 
!= None): 
 651             for element 
in complexType
.elements
: 
 653                     elementName 
= _toAttrName(obj
, element
.name
) 
 654                     if ((elementName 
not in obj
.__dict
__) or (obj
.__dict
__[elementName
] == None)): 
 655                         langType 
= xsdToLangType(element
.type) 
 656                         defaultValue 
= _objectfactory(langType
, element
.default
) 
 657                         obj
.__dict
__[elementName
] = defaultValue
 
 660         if (isinstance(obj
, list)): 
 661             if ((element
.attrs
.has_key("mutable")) and (element
.attrs
.getValue("mutable") == "false")): 
 665         if (len(self
.elementstack
) > 0): 
 666 ##            print "[endElement] appending child with name: ", name, "; objtype: ", objtype 
 667             parentElement
.children
.append((name
, obj
)) 
 669             self
.rootelement 
= obj
 
 671     def getRootObject(self
): 
 672         return self
.rootelement
 
 674 def _toAttrName(obj
, name
): 
 675     if (hasattr(obj
, "__xmlrename__")): 
 676         for key
, val 
in obj
.__xmlrename
__.iteritems(): 
 680 ##    if (name.startswith("__") and not name.endswith("__")): 
 681 ##        name = "_%s%s" % (obj.__class__.__name__, name) 
 684 def printKnownTypes(kt
, where
): 
 685     print 'KnownTypes from %s' % (where
) 
 686     for tag
, cls 
in kt
.iteritems(): 
 687         print '%s => %s' % (tag
, str(cls
)) 
 689 __typeMappingXsdToLang 
= { 
 693     "date": "str", # ToDO Need to work out how to create lang date types 
 695     "decimal": "float", # ToDO Does python have a better fixed point type? 
 702     "unicode": "unicode", 
 704     "duration": "str", # see above (date) 
 705     "datetime": "str", # see above (date) 
 706     "time": "str", # see above (date) 
 709     "blob" : "str",         # ag:blob 
 710     "currency" : "str",     # ag:currency 
 713 def xsdToLangType(xsdType
): 
 714     if xsdType
.startswith(XMLSCHEMA_XSD_URL
): 
 715         xsdType 
= xsdType
[len(XMLSCHEMA_XSD_URL
)+1:] 
 716     elif xsdType
.startswith(AG_URL
): 
 717         xsdType 
= xsdType
[len(AG_URL
)+1:] 
 718     langType 
= __typeMappingXsdToLang
.get(xsdType
) 
 719     if (langType 
== None): 
 720         raise Exception("Unknown xsd type %s" % xsdType
) 
 723 def langToXsdType(langType
): 
 724     if langType 
in asDict(__typeMappingXsdToLang
): 
 725         return '%s:%s' % (XMLSCHEMA_XSD_URL
, langType
) 
 728 def _getXmlValue(langValue
): 
 729     if (isinstance(langValue
, bool)): 
 730         return str(langValue
).lower() 
 731     elif (isinstance(langValue
, unicode)): 
 732         return langValue
.encode() 
 734         return str(langValue
) 
 736 def unmarshal(xmlstr
, knownTypes
=None, knownNamespaces
=None, xmlSource
=None, createGenerics
=False): 
 737     objectfactory 
= XMLObjectFactory(knownTypes
, knownNamespaces
, xmlSource
, createGenerics
) 
 738     # on Linux, pyXML's sax.parseString fails when passed unicode 
 739     if (not sysutils
.isWindows()): 
 742         xml
.sax
.parseString(xmlstr
, objectfactory
) 
 743     except xml
.sax
.SAXParseException
, errorData
: 
 744         if xmlSource 
== None: 
 745             xmlSource 
= 'unknown' 
 746         errorString 
= 'SAXParseException ("%s") detected at line %d, column %d in XML document from source "%s" ' % (errorData
.getMessage(), errorData
.getLineNumber(), errorData
.getColumnNumber(), xmlSource
) 
 747         raise UnmarshallerException(errorString
) 
 748     return objectfactory
.getRootObject() 
 750 def marshal(obj
, elementName
=None, prettyPrint
=False, marshalType
=True, indent
=0, knownTypes
=None, knownNamespaces
=None, encoding
=-1): 
 751     worker 
= XMLMarshalWorker(prettyPrint
=prettyPrint
, marshalType
=marshalType
, knownTypes
=knownTypes
, knownNamespaces
=knownNamespaces
)     
 752     if obj 
!= None and hasattr(obj
, '__xmldeepexclude__'): 
 753         worker
.xmldeepexclude 
= obj
.__xmldeepexclude
__ 
 754     xmlstr 
= "".join(worker
._marshal
(obj
, elementName
, indent
=indent
)) 
 755     aglogging
.info(xmlMarshallerLogger
, "marshal produced string of type %s", type(xmlstr
)) 
 756     if (encoding 
== None): 
 758     if (not isinstance(encoding
, basestring
)): 
 759         encoding 
= sys
.getdefaultencoding() 
 760     if (not isinstance(xmlstr
, unicode)): 
 761         xmlstr 
= xmlstr
.decode() 
 762     xmlstr 
= u
'<?xml version="1.0" encoding="%s"?>\n%s' % (encoding
, xmlstr
) 
 763     return xmlstr
.encode(encoding
) 
 765 class XMLMarshalWorker(object): 
 766     def __init__(self
, marshalType
=True, prettyPrint
=False, knownTypes
=None, knownNamespaces
=None): 
 767         if knownTypes 
== None: 
 770             self
.knownTypes 
= knownTypes
 
 771         if knownNamespaces 
== None: 
 772             self
.knownNamespaces 
= {} 
 774             self
.knownNamespaces 
= knownNamespaces
 
 775         self
.prettyPrint 
= prettyPrint
 
 776         self
.marshalType 
= marshalType
 
 777         self
.xmldeepexclude 
= [] 
 780     def getNSPrefix(self
): 
 781         if len(self
.nsstack
) > 0: 
 782             return self
.nsstack
[-1].prefix
 
 785     def isKnownType(self
, elementName
): 
 787         nse 
= self
.nsstack
[-1] 
 788         i 
= elementName
.rfind(':') 
 790             prefix 
= elementName
[:i
] 
 791             name 
= elementName
[i
+1:] 
 793             prefix 
= DEFAULT_NAMESPACE_KEY
 
 795         for shortNs
, longNs 
in nse
.nameSpaces
.iteritems(): 
 796             if shortNs 
== prefix
: 
 799         if tagLongNs 
== None: 
 800             knownTagName 
= elementName
 
 802             knownShortNs 
= self
.knownNamespaces
[tagLongNs
] 
 803             knownTagName 
= knownShortNs 
+ ':' + name
 
 804         if (knownTagName 
in asDict(self
.knownTypes
)): 
 805             knownClass 
= self
.knownTypes
[knownTagName
] 
 809     def popNSStack(self
): 
 812     def appendNSStack(self
, obj
): 
 815         for nse 
in self
.nsstack
: 
 816             for k
, v 
in nse
.nsMap
.iteritems(): 
 818                 if k 
== DEFAULT_NAMESPACE_KEY
: 
 822         if hasattr(obj
, "__xmlnamespaces__"): 
 823             ns 
= getattr(obj
, "__xmlnamespaces__") 
 826             for nameSpaceKey 
in keys
: 
 827                 nameSpaceUrl 
= ns
[nameSpaceKey
] 
 828                 if nameSpaceUrl 
in nameSpaces
.values(): 
 829                     for k
, v 
in nameSpaces
.iteritems(): 
 830                         if v 
== nameSpaceUrl
: 
 834                     if nameSpaceKey 
== "": 
 835                         defaultLongNS 
= nameSpaceUrl
 
 836                         nameSpaces
[DEFAULT_NAMESPACE_KEY
] = nameSpaceUrl
 
 837                         newNS
.nsMap
[DEFAULT_NAMESPACE_KEY
] = nameSpaceUrl
 
 838                         nameSpaceAttrs 
+= ' xmlns="%s" ' % (nameSpaceUrl
) 
 840                         nameSpaces
[nameSpaceKey
] = nameSpaceUrl
 
 841                         newNS
.nsMap
[nameSpaceKey
] = nameSpaceUrl
 
 842                         nameSpaceAttrs 
+= ' xmlns:%s="%s" ' % (nameSpaceKey
, nameSpaceUrl
) 
 843             nameSpaceAttrs 
= nameSpaceAttrs
.rstrip() 
 844         if len(self
.nsstack
) > 0: 
 845             newNS
.prefix 
= self
.nsstack
[-1].prefix
 
 848         if obj 
!= None and hasattr(obj
, "__xmldefaultnamespace__"): 
 849             longPrefixNS 
= getattr(obj
, "__xmldefaultnamespace__") 
 850             if longPrefixNS 
== defaultLongNS
: 
 854                     for k
, v 
in nameSpaces
.iteritems(): 
 855                         if v 
== longPrefixNS
: 
 856                             newNS
.prefix 
= k 
+ ':' 
 859                     if (longPrefixNS 
in asDict(self
.knownNamespaces
)): 
 860                         newNS
.prefix 
= self
.knownNamespaces
[longPrefixNS
] + ':' 
 862                         raise MarshallerException('Error marshalling __xmldefaultnamespace__ ("%s") not defined in namespace stack' % (longPrefixNS
)) 
 863         if obj 
!= None and hasattr(obj
, "targetNamespace"): 
 864             newNS
.targetNS 
= obj
.targetNamespace
 
 865         elif len(self
.nsstack
) > 0: 
 866             newNS
.targetNS 
= self
.nsstack
[-1].targetNS
 
 867         newNS
.nameSpaces 
= nameSpaces
 
 868         self
.nsstack
.append(newNS
) 
 869         return nameSpaceAttrs       
 
 871     def contractQName(self
, value
, obj
, attr
): 
 872         value 
= langToXsdType(value
) 
 877             # the value doesn't have a namespace and we couldn't map it to an XSD type...what to do? 
 878             # (a) just write it, as is, and hope it's in the default namespace (for now) 
 879             # (b) throw an exception so we can track down the bad code (later) 
 881         if (longNS 
in self
.nsstack
[-1].nameSpaces
.values()): 
 882             for kShort
, vLong 
in self
.nsstack
[-1].nameSpaces
.iteritems(): 
 887             shortNS 
= longNS    
# if we can't find the long->short mappping, just use longNS 
 888         if shortNS 
== DEFAULT_NAMESPACE_KEY
: 
 891             value 
= shortNS 
+ ':' + value
[i
+1:] 
 894     def _genObjTypeStr(self
, typeString
): 
 896             return ' objtype="%s"' % typeString
 
 899     def _marshal(self
, obj
, elementName
=None, nameSpacePrefix
="", indent
=0): 
 901             aglogging
.debug(xmlMarshallerLogger
, "--> _marshal: elementName=%s%s, type=%s, obj=%s, indent=%d", nameSpacePrefix
, elementName
, type(obj
), str(obj
), indent
) 
 903             aglogging
.debug(xmlMarshallerLogger
, "--> _marshal: elementName=%s%s, obj is None, indent=%d", nameSpacePrefix
, elementName
, indent
) 
 904         if ((obj 
!= None) and (hasattr(obj
, 'preMarshal'))): 
 907         excludeAttrs
.extend(self
.xmldeepexclude
) 
 908         if hasattr(obj
, "__xmlexclude__"): 
 909             excludeAttrs
.extend(obj
.__xmlexclude
__) 
 910         prettyPrint 
= self
.prettyPrint
 
 911         knownTypes 
= self
.knownTypes
 
 913         if self
.prettyPrint 
or indent
: 
 921         ## Determine the XML element name. If it isn"t specified in the 
 922         ## parameter list, look for it in the __xmlname__ attribute, 
 923         ## else use the default generic BASETYPE_ELEMENT_NAME. 
 924         nameSpaceAttrs 
= self
.appendNSStack(obj
) 
 925         nameSpacePrefix 
= self
.getNSPrefix()        
 927             if hasattr(obj
, "__xmlname__"): 
 928                 elementName 
= nameSpacePrefix 
+ obj
.__xmlname
__ 
 930                 elementName 
= nameSpacePrefix 
+ BASETYPE_ELEMENT_NAME
 
 932             elementName 
= nameSpacePrefix 
+ elementName
 
 934         if (hasattr(obj
, "__xmlsequencer__")) and (obj
.__xmlsequencer
__ != None): 
 935             if (XMLSCHEMA_XSD_URL 
in self
.nsstack
[-1].nameSpaces
.values()): 
 936                 for kShort
, vLong 
in self
.nsstack
[-1].nameSpaces
.iteritems(): 
 937                     if vLong 
== XMLSCHEMA_XSD_URL
: 
 938                         if kShort 
!= DEFAULT_NAMESPACE_KEY
: 
 939                             xsdPrefix 
= kShort 
+ ':' 
 945             elementAdd 
= xsdPrefix 
+ obj
.__xmlsequencer
__ 
 950         ## Add more members_to_skip based on ones the user has selected 
 951         ## via the __xmlexclude__ and __xmldeepexclude__ attributes. 
 952         members_to_skip
.extend(excludeAttrs
) 
 953         # Marshal the attributes that are selected to be XML attributes. 
 955         className 
= ag_className(obj
) 
 956         classNamePrefix 
= "_" + className
 
 957         if hasattr(obj
, "__xmlattributes__"): 
 958             xmlattributes 
= obj
.__xmlattributes
__ 
 959             members_to_skip
.extend(xmlattributes
) 
 960             for attr 
in xmlattributes
: 
 961                 internalAttrName 
= attr
 
 963                 if (attr
.startswith("__") and not attr
.endswith("__")):  
 964                     internalAttrName 
= classNamePrefix 
+ attr
 
 966                 # Fail silently if a python attribute is specified to be 
 967                 # an XML attribute but is missing. 
 968                 attrNameSpacePrefix 
= "" 
 969                 if hasattr(obj
, "__xmlattrnamespaces__"): 
 970                     for nameSpaceKey
, nameSpaceAttributes 
in getattr(obj
, "__xmlattrnamespaces__").iteritems(): 
 971                         if nameSpaceKey 
== nameSpacePrefix
[:-1]: # Don't need to specify attribute namespace if it is the same as its element 
 973                         if attr 
in nameSpaceAttributes
: 
 974                             attrNameSpacePrefix 
= nameSpaceKey 
+ ":" 
 977                 value 
= attrs
.get(internalAttrName
) 
 978                 if (hasattr(obj
, "__xmlrename__") and attr 
in asDict(obj
.__xmlrename
__)): 
 979                     attr 
= obj
.__xmlrename
__[attr
] 
 981                 complexType 
= getComplexType(obj
) 
 982                 if (complexType 
!= None): 
 983                     xsdElement 
= complexType
.findElement(attr
) 
 984                 if (xsdElement 
!= None): 
 985                     default 
= xsdElement
.default
 
 986                     if (default 
!= None): 
 987                         if ((default 
== value
) or (default 
== _getXmlValue(value
))): 
 992                         elif xsdElement
.type == TYPE_QNAME
: 
 993                             value 
= self
.contractQName(value
, obj
, attr
) 
 997                 # ToDO remove maxOccurs hack after bug 177 is fixed 
 998                 if attr 
== "maxOccurs" and value 
== -1: 
1001                 if isinstance(value
, bool): 
1007                     value 
= objutils
.toDiffableRepr(value
) 
1009                 objattrs 
+= ' %s%s="%s"' % (attrNameSpacePrefix
, attr
, utillang
.escape(value
)) 
1012         elif isinstance(obj
, bool): 
1013             objTypeStr 
= self
._genObjTypeStr
("bool") 
1014             xmlString 
= ['%s<%s%s>%s</%s>%s' % (prefix
, elementName
, objTypeStr
, obj
, elementName
, newline
)] 
1015         elif isinstance(obj
, int): 
1016             objTypeStr 
= self
._genObjTypeStr
("int") 
1017             xmlString 
= ['%s<%s%s>%s</%s>%s' % (prefix
, elementName
, objTypeStr
, str(obj
), elementName
, newline
)] 
1018         elif isinstance(obj
, long): 
1019             objTypeStr 
= self
._genObjTypeStr
("long") 
1020             xmlString 
= ['%s<%s%s>%s</%s>%s' % (prefix
, elementName
, objTypeStr
, str(obj
), elementName
, newline
)] 
1021         elif isinstance(obj
, float): 
1022             objTypeStr 
= self
._genObjTypeStr
("float") 
1023             xmlString 
= ['%s<%s%s>%s</%s>%s' % (prefix
, elementName
, objTypeStr
, str(obj
), elementName
, newline
)] 
1024         elif isinstance(obj
, unicode): # have to check before basestring - unicode is instance of base string 
1025             xmlString 
= ['%s<%s>%s</%s>%s' % (prefix
, elementName
, utillang
.escape(obj
.encode()), elementName
, newline
)] 
1026         elif isinstance(obj
, basestring
): 
1027             xmlString 
= ['%s<%s>%s</%s>%s' % (prefix
, elementName
, utillang
.escape(obj
), elementName
, newline
)] 
1028         elif isinstance(obj
, datetime
.datetime
): 
1029             objTypeStr 
= self
._genObjTypeStr
("datetime") 
1030             xmlString 
= ['%s<%s%s>%s</%s>%s' % (prefix
, elementName
, objTypeStr
, str(obj
), elementName
, newline
)] 
1031         elif isinstance(obj
, datetime
.date
): 
1032             objTypeStr 
= self
._genObjTypeStr
("date") 
1033             xmlString 
= ['%s<%s%s>%s</%s>%s' % (prefix
, elementName
, objTypeStr
, str(obj
), elementName
, newline
)] 
1034         elif isinstance(obj
, datetime
.time
): 
1035             objTypeStr 
= self
._genObjTypeStr
("time") 
1036             xmlString 
= ['%s<%s%s>%s</%s>%s' % (prefix
, elementName
, objTypeStr
, str(obj
), elementName
, newline
)] 
1037         elif isinstance(obj
, list): 
1041                 objTypeStr 
= self
._genObjTypeStr
("list") 
1042                 xmlString 
= ['%s<%s%s>%s' % (prefix
, elementName
, objTypeStr
, newline
)] 
1044                     xmlString
.extend(self
._marshal
(item
, indent
=indent
+increment
)) 
1045                 xmlString
.append("%s</%s>%s" % (prefix
, elementName
, newline
)) 
1046         elif isinstance(obj
, tuple): 
1050                 objTypeStr 
= self
._genObjTypeStr
("list") 
1051                 xmlString 
= ['%s<%s%s mutable="false">%s' % (prefix
, elementName
, objTypeStr
, newline
)] 
1053                     xmlString
.extend(self
._marshal
(item
, indent
=indent
+increment
)) 
1054                 xmlString
.append("%s</%s>%s" % (prefix
, elementName
, newline
)) 
1055         elif isinstance(obj
, dict): 
1056             objTypeStr 
= self
._genObjTypeStr
("dict") 
1057             xmlString 
= ['%s<%s%s>%s' % (prefix
, elementName
, objTypeStr
, newline
)] 
1058             subprefix 
= prefix 
+ " "*increment
 
1059             subindent 
= indent 
+ 2*increment
 
1063                 xmlString
.append("%s<%s>%s" % (subprefix
, DICT_ITEM_NAME
, newline
)) 
1064                 xmlString
.extend(self
._marshal
(key
, elementName
=DICT_ITEM_KEY_NAME
, indent
=subindent
)) 
1065                 xmlString
.extend(self
._marshal
(obj
[key
], elementName
=DICT_ITEM_VALUE_NAME
, indent
=subindent
)) 
1066                 xmlString
.append("%s</%s>%s" % (subprefix
, DICT_ITEM_NAME
, newline
)) 
1067             xmlString
.append("%s</%s>%s" % (prefix
, elementName
, newline
)) 
1068         elif hasattr(obj
, "__xmlcontent__"): 
1069             contentValue 
= getattr(obj
, obj
.__xmlcontent
__) 
1070             if contentValue 
== None:  
1071                 xmlString 
= ["%s<%s%s%s/>%s" % (prefix
, elementName
, nameSpaceAttrs
, objattrs
, newline
)]         
1073                 contentValue 
= utillang
.escape(contentValue
) 
1074                 xmlString 
= ["%s<%s%s%s>%s</%s>%s" % (prefix
, elementName
, nameSpaceAttrs
, objattrs
, contentValue
, elementName
, newline
)]         
1076             # Only add the objtype if the element tag is unknown to us. 
1077             if (isinstance(obj
, GenericXMLObject
)): 
1079             elif (self
.isKnownType(elementName
) == True): 
1082                 objTypeStr 
= self
._genObjTypeStr
("%s.%s" % (obj
.__class
__.__module
__, className
)) 
1083             xmlString 
= ['%s<%s%s%s%s' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
, objTypeStr
)] 
1084             # get the member, value pairs for the object, filtering out the types we don"t support 
1085             if (elementAdd 
!= None): 
1086                 prefix 
+= increment
*" " 
1088             xmlMemberString 
= [] 
1089             if hasattr(obj
, "__xmlbody__"): 
1090                 xmlbody 
= getattr(obj
, obj
.__xmlbody
__) 
1092                     xmlMemberString
.append(utillang
.escape(xmlbody
)) 
1094                 if hasattr(obj
, "__xmlattrgroups__"): 
1095                     attrGroups 
= obj
.__xmlattrgroups
__.copy() 
1096                     if (not isinstance(attrGroups
, dict)): 
1097                         raise "__xmlattrgroups__ is not a dict, but must be" 
1098                     for n 
in attrGroups
.iterkeys(): 
1099                         members_to_skip
.extend(attrGroups
[n
]) 
1102                 # add the list of all attributes to attrGroups 
1103                 eList 
= obj
.__dict
__.keys()     
1105                 attrGroups
["__nogroup__"] = eList
 
1107                 for eName
, eList 
in attrGroups
.iteritems(): 
1108                     if (eName 
!= "__nogroup__"): 
1109                         prefix 
+= increment
*" " 
1111                         objTypeStr 
= self
._genObjTypeStr
("None") 
1112                         xmlMemberString
.append('%s<%s%s>%s' % (prefix
, eName
, objTypeStr
, newline
)) 
1114                         value 
= obj
.__dict
__[name
] 
1115                         if eName 
== "__nogroup__" and name 
in members_to_skip
: continue 
1116                         if name
.startswith("__") and name
.endswith("__"): continue 
1117                         if (hasattr(obj
, "__xmlcdatacontent__") and (obj
.__xmlcdatacontent
__ == name
)): 
1119                         subElementNameSpacePrefix 
= nameSpacePrefix
 
1120                         if hasattr(obj
, "__xmlattrnamespaces__"): 
1121                             for nameSpaceKey
, nameSpaceValues 
in getattr(obj
, "__xmlattrnamespaces__").iteritems(): 
1122                                 if name 
in nameSpaceValues
: 
1123                                     subElementNameSpacePrefix 
= nameSpaceKey 
+ ":" 
1125                         # handle sequences listed in __xmlflattensequence__ 
1126                         # specially: instead of listing the contained items inside 
1127                         # of a separate list, as God intended, list them inside 
1128                         # the object containing the sequence. 
1129                         if (hasattr(obj
, "__xmlflattensequence__") and (value 
!= None) and (name 
in asDict(obj
.__xmlflattensequence
__))): 
1130                             xmlnametuple 
= obj
.__xmlflattensequence
__[name
] 
1131                             if (xmlnametuple 
== None): 
1132                                 xmlnametuple 
= [name
] 
1133                             elif (not isinstance(xmlnametuple
, (tuple,list))): 
1134                                 xmlnametuple 
= [str(xmlnametuple
)] 
1136                             if (len(xmlnametuple
) == 1): 
1137                                 xmlname 
= xmlnametuple
[0] 
1138                             if not isinstance(value
, (list, tuple)): 
1140                             for seqitem 
in value
: 
1141                                 xmlMemberString
.extend(self
._marshal
(seqitem
, xmlname
, subElementNameSpacePrefix
, indent
=indent
+increment
)) 
1143                             if (hasattr(obj
, "__xmlrename__") and name 
in asDict(obj
.__xmlrename
__)): 
1144                                 xmlname 
= obj
.__xmlrename
__[name
] 
1148                                 xmlMemberString
.extend(self
._marshal
(value
, xmlname
, subElementNameSpacePrefix
, indent
=indent
+increment
)) 
1149                     if (eName 
!= "__nogroup__"): 
1150                         xmlMemberString
.append("%s</%s>%s" % (prefix
, eName
, newline
)) 
1151                         prefix 
= prefix
[:-increment
] 
1154             # if we have nested elements, add them here, otherwise close the element tag immediately. 
1156             for s 
in xmlMemberString
: 
1157                 if (len(s
) > 0): newList
.append(s
) 
1158             xmlMemberString 
= newList
 
1159             if len(xmlMemberString
) > 0: 
1160                 xmlString
.append(">") 
1161                 if hasattr(obj
, "__xmlbody__"): 
1162                     xmlString
.extend(xmlMemberString
) 
1163                     xmlString
.append("</%s>%s" % (elementName
, newline
)) 
1165                     xmlString
.append(newline
) 
1166                     if (elementAdd 
!= None): 
1167                         xmlString
.append("%s<%s>%s" % (prefix
, elementAdd
, newline
)) 
1168                     xmlString
.extend(xmlMemberString
) 
1169                     if (elementAdd 
!= None): 
1170                         xmlString
.append("%s</%s>%s" % (prefix
, elementAdd
, newline
)) 
1171                         prefix 
= prefix
[:-increment
] 
1173                     xmlString
.append("%s</%s>%s" % (prefix
, elementName
, newline
)) 
1175                 if hasattr(obj
, "__xmlcdatacontent__"): 
1176                     cdataAttr 
= obj
.__xmlcdatacontent
__ 
1177                     cdataContent 
= obj
.__dict
__[cdataAttr
] 
1178                     xmlString
.append("><![CDATA[%s]]></%s>%s" % (cdataContent
, elementName
, newline
)) 
1180                     xmlString
.append("/>%s" % newline
) 
1181         if aglogging
.isEnabledForDebug(xmlMarshallerLogger
): 
1182             aglogging
.debug(xmlMarshallerLogger
, "<-- _marshal: %s", objutils
.toDiffableString(xmlString
)) 
1183         #print "<-- _marshal: %s" % str(xmlString) 
1187 # A simple test, to be executed when the xmlmarshaller is run standalone 
1188 class MarshallerPerson
: 
1189     __xmlname__ 
= "person" 
1190     __xmlexclude__ 
= ["fabulousness",] 
1191     __xmlattributes__ 
= ("nonSmoker",) 
1192     __xmlrename__ 
= {"_phoneNumber": "telephone"}
 
1193     __xmlflattensequence__ 
= {"favoriteWords": ("vocabulary",)}
 
1194     __xmlattrgroups__ 
= {"name": ["firstName", "lastName"], "address": ["addressLine1", "city", "state", "zip"]}
 
1196     def setPerson(self
): 
1197         self
.firstName 
= "Albert" 
1198         self
.lastName 
= "Camus" 
1199         self
.addressLine1 
= "23 Absurd St." 
1203         self
._phoneNumber 
= "808-303-2323" 
1204         self
.favoriteWords 
= ["angst", "ennui", "existence"] 
1205         self
.phobias 
= ["war", "tuberculosis", "cars"] 
1207         self
.fabulousness 
= "tres tres" 
1208         self
.nonSmoker 
= False 
1210 if isMain(__name__
): 
1211     p1 
= MarshallerPerson() 
1213     xmlP1 
= marshal(p1
, prettyPrint
=True, encoding
="utf-8")         
1214     print "\n########################" 
1215     print   "# testPerson test case #" 
1216     print   "########################" 
1218     p2 
= unmarshal(xmlP1
) 
1219     xmlP2 
= marshal(p2
, prettyPrint
=True, encoding
="utf-8") 
1221         print "Success: repeated marshalling yields identical results" 
1223         print "Failure: repeated marshalling yields different results"