]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/util/xmlmarshaller.py
DocView and ActiveGrid IDE updates from Morgan Hua:
[wxWidgets.git] / wxPython / samples / ide / activegrid / util / xmlmarshaller.py
1 #----------------------------------------------------------------------------
2 # Name: xmlmarshaller.py
3 # Purpose:
4 #
5 # Authors: John Spurling, Joel Hare, Jeff Norton, Alan Mullendore
6 #
7 # Created: 7/28/04
8 # CVS-ID: $Id$
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12 import sys
13 from types import *
14 from activegrid.util.lang import *
15 import logging
16 ifDefPy()
17 import xml.sax
18 import xml.sax.handler
19 import xml.sax.saxutils
20 import datetime
21 endIfDef()
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
26
27 MODULE_PATH = "__main__"
28
29 ## ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
30 ##unboundedVal = 2147483647 # value used for maxOccurs == "unbounded"
31
32 """
33 Special attributes that we recognize:
34
35 name: __xmlname__
36 type: string
37 description: the name of the xml element for the marshalled object
38
39 name: __xmlattributes__
40 type: tuple or list
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.
45
46 name: __xmlexclude__
47 type: tuple or list
48 description: the name(s) of the lang attribute(s) to skip when
49 marshalling.
50
51 name: __xmlrename__
52 type: dict
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
56 associated XML names.
57
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.:
64
65 myseq = (1, 2)
66 <!-- normal way of marshalling -->
67 <myseq>
68 <item objtype="int">1</item>
69 <item objtype="int">2</item>
70 </myseq>
71 <!-- with __xmlflattensequence__ set to {"myseq": "squish"} -->
72 <squish objtype="int">1</squish>
73 <squish objtype="int">2</squish>
74
75 name: __xmlnamespaces__
76 type: dict
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.:
80
81 __xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" }
82
83 name: __xmldefaultnamespace__
84 type: String
85 description: the prefix of a namespace defined in __xmlnamespaces__ that
86 should be used as the default namespace for the object.
87
88 name: __xmlattrnamespaces__
89 type: dict
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.:
94
95 __xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] }
96
97 name: __xmlattrgroups__
98 type: dict
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
101 within it. e.g.
102
103 __xmlattrgroups__ = {"name": ["firstName", "lastName"], "address": ["addressLine1", "city", "state", "zip"]}
104
105 name: __xmlcdatacontent__
106 type: string
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.
109
110 __xmlcdatacontent__ = "messyContent"
111
112 """
113
114 global xmlMarshallerLogger
115 xmlMarshallerLogger = logging.getLogger("activegrid.util.xmlmarshaller.marshal")
116 # INFO : low-level info
117 # DEBUG : debugging info
118
119 ################################################################################
120 #
121 # module exceptions
122 #
123 ################################################################################
124
125 class Error(Exception):
126 """Base class for errors in this module."""
127 pass
128
129 class UnhandledTypeException(Error):
130 """Exception raised when attempting to marshal an unsupported
131 type.
132 """
133 def __init__(self, typename):
134 self.typename = typename
135 def __str__(self):
136 return "%s is not supported for marshalling." % str(self.typename)
137
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
141 a nested element.
142 """
143 def __init__(self, attrname, typename):
144 self.attrname = attrname
145 self.typename = typename
146 def __str__(self):
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)
150
151 class MarshallerException(Exception):
152 pass
153
154 class UnmarshallerException(Exception):
155 pass
156
157 ################################################################################
158 #
159 # constants and such
160 #
161 ################################################################################
162
163 XMLNS = "xmlns"
164 XMLNS_PREFIX = XMLNS + ":"
165 XMLNS_PREFIX_LENGTH = len(XMLNS_PREFIX)
166 DEFAULT_NAMESPACE_KEY = "__DEFAULTNS__"
167 TYPE_QNAME = "QName"
168 XMLSCHEMA_XSD_URL = "http://www.w3.org/2001/XMLSchema"
169 AG_URL = "http://www.activegrid.com/ag.xsd"
170
171 BASETYPE_ELEMENT_NAME = "item"
172 DICT_ITEM_NAME = "qqDictItem"
173 DICT_ITEM_KEY_NAME = "key"
174 DICT_ITEM_VALUE_NAME = "value"
175
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__")
182
183 ################################################################################
184 #
185 # classes and functions
186 #
187 ################################################################################
188
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
196 return
197 object.__dict__[name] = value
198
199 def getComplexType(obj):
200 if (hasattr(obj, "_instancexsdcomplextype")):
201 return obj._instancexsdcomplextype
202 if (hasattr(obj, "__xsdcomplextype__")):
203 return obj.__xsdcomplextype__
204 return None
205
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):
209 objargs = [objargs]
210 if (objclass != None):
211 obj = None
212 if (len(objargs) > 0):
213 if (hasattr(objclass, "__xmlcdatacontent__")):
214 obj = objclass()
215 contentAttr = obj.__xmlcdatacontent__
216 obj.__dict__[contentAttr] = str(objargs[0])
217 else:
218 obj = objclass(*objargs)
219 else:
220 obj = objclass()
221 if ((obj != None) and (hasattr(obj, 'postUnmarshal'))):
222 obj.postUnmarshal()
223 return obj
224 return objutils.newInstance(objtype, objargs)
225
226 class GenericXMLObject(object):
227 def __init__(self, content=None):
228 if content != None:
229 self._content = content
230 self.__xmlcontent__ = '_content'
231
232 def __str__(self):
233 return "GenericXMLObject(%s)" % objutils.toDiffableString(self.__dict__)
234
235 def setXMLAttributes(self, xmlName, attrs=None, children=None, nsMap=None, defaultNS=None):
236 if xmlName != None:
237 i = xmlName.rfind(':')
238 if i < 0:
239 self.__xmlname__ = xmlName
240 if defaultNS != None:
241 self.__xmldefaultnamespace__ = str(defaultNS)
242 else:
243 self.__xmlname__ = xmlName[i+1:]
244 prefix = xmlName[:i]
245 if nsMap.has_key(prefix):
246 self.__xmldefaultnamespace__ = str(nsMap[prefix])
247 if attrs != None:
248 for attrname, attr in attrs.items():
249 attrname = str(attrname)
250 if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
251 pass
252 elif attrname == "objtype":
253 pass
254 else:
255 if not hasattr(self, '__xmlattributes__'):
256 self.__xmlattributes__ = []
257 i = attrname.rfind(':')
258 if i >= 0:
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]
265 else:
266 alist = []
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:
273 childList = []
274 flattenList = {}
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,)
280 else:
281 childList.append(childstr)
282 if len(flattenList) > 0:
283 self.__xmlflattensequence__ = flattenList
284
285 def initialize(self, arg1=None):
286 pass
287
288
289 class Element:
290 def __init__(self, name, attrs=None, xsname=None):
291 self.name = name
292 self.attrs = attrs
293 self.content = ""
294 self.children = []
295 self.objclass = None
296 self.xsname = xsname
297 self.objtype = None
298
299 def getobjtype(self):
300 # objtype = self.attrs.get("objtype")
301 objtype = self.objtype
302 if (objtype == None):
303 if (len(self.children) > 0):
304 objtype = "dict"
305 else:
306 objtype = "str"
307 return objtype
308
309 class NsElement(object):
310 def __init__(self):
311 self.nsMap = {}
312 self.targetNS = None
313 self.defaultNS = None
314 self.prefix = None
315
316 def __str__(self):
317 if self.prefix == None:
318 strVal = 'prefix = None; '
319 else:
320 strVal = 'prefix = "%s"; ' % (self.prefix)
321 if self.targetNS == None:
322 strVal += 'targetNS = None; '
323 else:
324 strVal += 'targetNS = "%s"; ' % (self.targetNS)
325 if self.defaultNS == None:
326 strVal += 'defaultNS = None; '
327 else:
328 strVal += 'defaultNS = "%s"; ' % (self.defaultNS)
329 if len(self.nsMap) == 0:
330 strVal += 'nsMap = None; '
331 else:
332 strVal += 'nsMap = {'
333 for ik, iv in self.nsMap.iteritems():
334 strVal += '%s=%s; ' % (ik,iv)
335 strVal += '}'
336 return strVal
337
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()
345 for tag in newKT:
346 if tag.find(':') < 0:
347 del self.knownTypes[tag]
348 newMap = parentNSE.nsMap.copy()
349 if self.nsMap != {}:
350 for k, v in self.nsMap.iteritems():
351 newMap[k] = v
352 self.nsMap = newMap
353 else:
354 self.knownTypes = {}
355 reversedKNS = {}
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():
363 i = tag.rfind(':')
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
374 break
375 if self.defaultNS == knownTagLong:
376 self.knownTypes[knownTagName] = mapClass
377 else: # e.g. "ItemSearchRequest"
378 self.knownTypes[tag] = mapClass
379
380 def expandQName(self, eName, attrName, attrValue):
381 bigValue = attrValue
382 i = attrValue.rfind(':')
383 if (i < 0):
384 if self.defaultNS != None:
385 bigValue = '%s:%s' % (self.defaultNS, attrValue)
386 else:
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)
392 break
393 return bigValue
394
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"
400 else:
401 self.xmlSource = xmlSource
402 self.createGenerics = createGenerics
403 self.skipper = False
404 self.elementstack = []
405 self.nsstack = []
406 self.collectContent = None
407 if (knownNamespaces == None):
408 self.knownNamespaces = {}
409 else:
410 self.knownNamespaces = knownNamespaces
411 self.reversedNamespaces = {}
412 for longns, shortns in self.knownNamespaces.iteritems():
413 self.reversedNamespaces[shortns] = longns
414 self.knownTypes = {}
415 if (knownTypes != None):
416 for tag, cls in knownTypes.iteritems():
417 i = tag.rfind(':')
418 if i >= 0:
419 shortns = tag[:i]
420 tag = tag[i+1:]
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)
429
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)
443 newNS.nsMap = map
444 self.nsstack.append(newNS)
445 return newNS
446
447 def popElementStack(self):
448 element = self.elementstack.pop()
449 nse = self.nsstack.pop()
450 return element, nse
451
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
456 self.skipper = True
457 self.appendElementStack(Element(name, attrs.copy()), NsElement())
458 if self.skipper:
459 return
460 if self.collectContent != None:
461 strVal = '<%s' % (name)
462 for aKey, aVal in attrs.items():
463 strVal += (' %s="%s"' % (aKey, aVal))
464 strVal += '>'
465 self.collectContent.content += strVal
466 xsname = name
467 i = name.rfind(':')
468 if i >= 0:
469 nsname = name[:i]
470 name = name[i+1:]
471 else:
472 nsname = None
473 element = Element(name, attrs.copy(), xsname=xsname)
474 # if the element has namespace attributes, process them and add them to our stack
475 nse = NsElement()
476 objtype = None
477 for k in attrs.getNames():
478 if k.startswith('xmlns'):
479 longNs = attrs[k]
480 eLongNs = longNs + '/'
481 if str(eLongNs) in asDict(self.knownNamespaces):
482 longNs = eLongNs
483 if k == 'xmlns':
484 nse.defaultNS = longNs
485 else:
486 shortNs = k[6:]
487 nse.nsMap[shortNs] = longNs
488 elif k == 'targetNamespace':
489 nse.targetNS = attrs.getValue(k)
490 elif k == 'objtype':
491 objtype = attrs.getValue(k)
492 nse = self.appendElementStack(element, nse)
493 if nsname != None:
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)
501 else:
502 longname = xsname
503 elif nse.defaultNS != None:
504 longname = '%s:%s' % (nse.defaultNS, name)
505 else:
506 longname = 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
514
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
520 else:
521 self.elementstack[-1].content += content
522
523 def endElement(self, name):
524 ## print "[endElement] </%s>" % name
525 xsname = name
526 i = name.rfind(':')
527 if i >= 0: # Strip namespace prefixes for now until actually looking them up in xsd
528 name = name[i+1:]
529 if self.skipper:
530 if xsname == "xs:annotation" or xsname == "xsd:annotation": # here too
531 self.skipper = False
532 self.popElementStack()
533 return
534 if self.collectContent != None:
535 if xsname != self.collectContent.xsname:
536 self.collectContent.content += ('</%s>' % (xsname))
537 self.popElementStack()
538 return
539 else:
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"):
549 return
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)
563 if (obj != None):
564 if (hasattr(obj, "__xmlname__") and getattr(obj, "__xmlname__") == "sequence"):
565 self.elementstack[-1].children = oldChildren
566 return
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:]
572 else:
573 ns = ""
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
589 if (type != None):
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":
595 attr = "-1"
596 try:
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)
601 try:
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__
608 flattenDict = {}
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
617 else:
618 for xmlname in xmlnametuple:
619 flattenDict[xmlname] = sequencename
620 else:
621 raise Exception("Invalid type for __xmlflattensequence___ : it must be a dict")
622
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"):
635 obj.append(child)
636 elif isinstance(obj, dict):
637 if (childname == DICT_ITEM_NAME):
638 obj[child[DICT_ITEM_KEY_NAME]] = child[DICT_ITEM_VALUE_NAME]
639 else:
640 obj[childname] = child
641 else:
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)):
645 try:
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))
649
650 if (complexType != None):
651 for element in complexType.elements:
652 if element.default:
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
658
659 ifDefPy()
660 if (isinstance(obj, list)):
661 if ((element.attrs.has_key("mutable")) and (element.attrs.getValue("mutable") == "false")):
662 obj = tuple(obj)
663 endIfDef()
664
665 if (len(self.elementstack) > 0):
666 ## print "[endElement] appending child with name: ", name, "; objtype: ", objtype
667 parentElement.children.append((name, obj))
668 else:
669 self.rootelement = obj
670
671 def getRootObject(self):
672 return self.rootelement
673
674 def _toAttrName(obj, name):
675 if (hasattr(obj, "__xmlrename__")):
676 for key, val in obj.__xmlrename__.iteritems():
677 if (name == val):
678 name = key
679 break
680 ## if (name.startswith("__") and not name.endswith("__")):
681 ## name = "_%s%s" % (obj.__class__.__name__, name)
682 return str(name)
683
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))
688
689 __typeMappingXsdToLang = {
690 "string": "str",
691 "char": "str",
692 "varchar": "str",
693 "date": "str", # ToDO Need to work out how to create lang date types
694 "boolean": "bool",
695 "decimal": "float", # ToDO Does python have a better fixed point type?
696 "int": "int",
697 "integer":"int",
698 "long": "long",
699 "float": "float",
700 "bool": "bool",
701 "str": "str",
702 "unicode": "unicode",
703 "short": "int",
704 "duration": "str", # see above (date)
705 "datetime": "str", # see above (date)
706 "time": "str", # see above (date)
707 "double": "float",
708 "QName" : "str",
709 "blob" : "str", # ag:blob
710 "currency" : "str", # ag:currency
711 }
712
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)
721 return langType
722
723 def langToXsdType(langType):
724 if langType in asDict(__typeMappingXsdToLang):
725 return '%s:%s' % (XMLSCHEMA_XSD_URL, langType)
726 return langType
727
728 def _getXmlValue(langValue):
729 if (isinstance(langValue, bool)):
730 return str(langValue).lower()
731 elif (isinstance(langValue, unicode)):
732 return langValue.encode()
733 else:
734 return str(langValue)
735
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()):
740 xmlstr = str(xmlstr)
741 try:
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()
749
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):
757 return xmlstr
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)
764
765 class XMLMarshalWorker(object):
766 def __init__(self, marshalType=True, prettyPrint=False, knownTypes=None, knownNamespaces=None):
767 if knownTypes == None:
768 self.knownTypes = {}
769 else:
770 self.knownTypes = knownTypes
771 if knownNamespaces == None:
772 self.knownNamespaces = {}
773 else:
774 self.knownNamespaces = knownNamespaces
775 self.prettyPrint = prettyPrint
776 self.marshalType = marshalType
777 self.xmldeepexclude = []
778 self.nsstack = []
779
780 def getNSPrefix(self):
781 if len(self.nsstack) > 0:
782 return self.nsstack[-1].prefix
783 return ''
784
785 def isKnownType(self, elementName):
786 tagLongNs = None
787 nse = self.nsstack[-1]
788 i = elementName.rfind(':')
789 if i > 0:
790 prefix = elementName[:i]
791 name = elementName[i+1:]
792 else:
793 prefix = DEFAULT_NAMESPACE_KEY
794 name = elementName
795 for shortNs, longNs in nse.nameSpaces.iteritems():
796 if shortNs == prefix:
797 tagLongNs = longNs
798 break
799 if tagLongNs == None:
800 knownTagName = elementName
801 else:
802 knownShortNs = self.knownNamespaces[tagLongNs]
803 knownTagName = knownShortNs + ':' + name
804 if (knownTagName in asDict(self.knownTypes)):
805 knownClass = self.knownTypes[knownTagName]
806 return True
807 return False
808
809 def popNSStack(self):
810 self.nsstack.pop()
811
812 def appendNSStack(self, obj):
813 nameSpaces = {}
814 defaultLongNS = None
815 for nse in self.nsstack:
816 for k, v in nse.nsMap.iteritems():
817 nameSpaces[k] = v
818 if k == DEFAULT_NAMESPACE_KEY:
819 defaultLongNS = v
820 newNS = NsElement()
821 nameSpaceAttrs = ""
822 if hasattr(obj, "__xmlnamespaces__"):
823 ns = getattr(obj, "__xmlnamespaces__")
824 keys = ns.keys()
825 keys.sort()
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:
831 nameSpaceKey = k
832 break
833 else:
834 if nameSpaceKey == "":
835 defaultLongNS = nameSpaceUrl
836 nameSpaces[DEFAULT_NAMESPACE_KEY] = nameSpaceUrl
837 newNS.nsMap[DEFAULT_NAMESPACE_KEY] = nameSpaceUrl
838 nameSpaceAttrs += ' xmlns="%s" ' % (nameSpaceUrl)
839 else:
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
846 else:
847 newNS.prefix = ''
848 if obj != None and hasattr(obj, "__xmldefaultnamespace__"):
849 longPrefixNS = getattr(obj, "__xmldefaultnamespace__")
850 if longPrefixNS == defaultLongNS:
851 newNS.prefix = ''
852 else:
853 try:
854 for k, v in nameSpaces.iteritems():
855 if v == longPrefixNS:
856 newNS.prefix = k + ':'
857 break;
858 except:
859 if (longPrefixNS in asDict(self.knownNamespaces)):
860 newNS.prefix = self.knownNamespaces[longPrefixNS] + ':'
861 else:
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
870
871 def contractQName(self, value, obj, attr):
872 value = langToXsdType(value)
873 i = value.rfind(':')
874 if i >= 0:
875 longNS = value[:i]
876 else:
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)
880 return value
881 if (longNS in self.nsstack[-1].nameSpaces.values()):
882 for kShort, vLong in self.nsstack[-1].nameSpaces.iteritems():
883 if vLong == longNS:
884 shortNS = kShort
885 break
886 else:
887 shortNS = longNS # if we can't find the long->short mappping, just use longNS
888 if shortNS == DEFAULT_NAMESPACE_KEY:
889 value = value[i+1:]
890 else:
891 value = shortNS + ':' + value[i+1:]
892 return value
893
894 def _genObjTypeStr(self, typeString):
895 if self.marshalType:
896 return ' objtype="%s"' % typeString
897 return ""
898
899 def _marshal(self, obj, elementName=None, nameSpacePrefix="", indent=0):
900 if (obj != None):
901 aglogging.debug(xmlMarshallerLogger, "--> _marshal: elementName=%s%s, type=%s, obj=%s, indent=%d", nameSpacePrefix, elementName, type(obj), str(obj), indent)
902 else:
903 aglogging.debug(xmlMarshallerLogger, "--> _marshal: elementName=%s%s, obj is None, indent=%d", nameSpacePrefix, elementName, indent)
904 if ((obj != None) and (hasattr(obj, 'preMarshal'))):
905 obj.preMarshal()
906 excludeAttrs = []
907 excludeAttrs.extend(self.xmldeepexclude)
908 if hasattr(obj, "__xmlexclude__"):
909 excludeAttrs.extend(obj.__xmlexclude__)
910 prettyPrint = self.prettyPrint
911 knownTypes = self.knownTypes
912 xmlString = None
913 if self.prettyPrint or indent:
914 prefix = " "*indent
915 newline = "\n"
916 increment = 2
917 else:
918 prefix = ""
919 newline = ""
920 increment = 0
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()
926 if not elementName:
927 if hasattr(obj, "__xmlname__"):
928 elementName = nameSpacePrefix + obj.__xmlname__
929 else:
930 elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
931 else:
932 elementName = nameSpacePrefix + elementName
933
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 + ':'
940 else:
941 xsdPrefix = ''
942 break
943 else:
944 xsdPrefix = 'xs:'
945 elementAdd = xsdPrefix + obj.__xmlsequencer__
946 else:
947 elementAdd = None
948
949 members_to_skip = []
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.
954 objattrs = ""
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
962 ifDefPy()
963 if (attr.startswith("__") and not attr.endswith("__")):
964 internalAttrName = classNamePrefix + attr
965 endIfDef()
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
972 continue
973 if attr in nameSpaceAttributes:
974 attrNameSpacePrefix = nameSpaceKey + ":"
975 break
976 attrs = obj.__dict__
977 value = attrs.get(internalAttrName)
978 if (hasattr(obj, "__xmlrename__") and attr in asDict(obj.__xmlrename__)):
979 attr = obj.__xmlrename__[attr]
980 xsdElement = None
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))):
988 continue
989 else:
990 if (value == None):
991 continue
992 elif xsdElement.type == TYPE_QNAME:
993 value = self.contractQName(value, obj, attr)
994 elif value == None:
995 continue
996
997 # ToDO remove maxOccurs hack after bug 177 is fixed
998 if attr == "maxOccurs" and value == -1:
999 value = "unbounded"
1000
1001 if isinstance(value, bool):
1002 if value == True:
1003 value = "true"
1004 else:
1005 value = "false"
1006 else:
1007 value = objutils.toDiffableRepr(value)
1008
1009 objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, utillang.escape(value))
1010 if (obj == None):
1011 xmlString = [""]
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):
1038 if len(obj) < 1:
1039 xmlString = ""
1040 else:
1041 objTypeStr = self._genObjTypeStr("list")
1042 xmlString = ['%s<%s%s>%s' % (prefix, elementName, objTypeStr, newline)]
1043 for item in obj:
1044 xmlString.extend(self._marshal(item, indent=indent+increment))
1045 xmlString.append("%s</%s>%s" % (prefix, elementName, newline))
1046 elif isinstance(obj, tuple):
1047 if len(obj) < 1:
1048 xmlString = ""
1049 else:
1050 objTypeStr = self._genObjTypeStr("list")
1051 xmlString = ['%s<%s%s mutable="false">%s' % (prefix, elementName, objTypeStr, newline)]
1052 for item in obj:
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
1060 keys = obj.keys()
1061 keys.sort()
1062 for key in keys:
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)]
1072 else:
1073 contentValue = utillang.escape(contentValue)
1074 xmlString = ["%s<%s%s%s>%s</%s>%s" % (prefix, elementName, nameSpaceAttrs, objattrs, contentValue, elementName, newline)]
1075 else:
1076 # Only add the objtype if the element tag is unknown to us.
1077 if (isinstance(obj, GenericXMLObject)):
1078 objTypeStr = ""
1079 elif (self.isKnownType(elementName) == True):
1080 objTypeStr = ""
1081 else:
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*" "
1087 indent += increment
1088 xmlMemberString = []
1089 if hasattr(obj, "__xmlbody__"):
1090 xmlbody = getattr(obj, obj.__xmlbody__)
1091 if xmlbody != None:
1092 xmlMemberString.append(utillang.escape(xmlbody))
1093 else:
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])
1100 else:
1101 attrGroups = {}
1102 # add the list of all attributes to attrGroups
1103 eList = obj.__dict__.keys()
1104 eList.sort()
1105 attrGroups["__nogroup__"] = eList
1106
1107 for eName, eList in attrGroups.iteritems():
1108 if (eName != "__nogroup__"):
1109 prefix += increment*" "
1110 indent += increment
1111 objTypeStr = self._genObjTypeStr("None")
1112 xmlMemberString.append('%s<%s%s>%s' % (prefix, eName, objTypeStr, newline))
1113 for name in eList:
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)):
1118 continue
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 + ":"
1124 break
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)]
1135 xmlname = None
1136 if (len(xmlnametuple) == 1):
1137 xmlname = xmlnametuple[0]
1138 if not isinstance(value, (list, tuple)):
1139 value = [value]
1140 for seqitem in value:
1141 xmlMemberString.extend(self._marshal(seqitem, xmlname, subElementNameSpacePrefix, indent=indent+increment))
1142 else:
1143 if (hasattr(obj, "__xmlrename__") and name in asDict(obj.__xmlrename__)):
1144 xmlname = obj.__xmlrename__[name]
1145 else:
1146 xmlname = name
1147 if (value != None):
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]
1152 indent -= increment
1153
1154 # if we have nested elements, add them here, otherwise close the element tag immediately.
1155 newList = []
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))
1164 else:
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]
1172 indent -= increment
1173 xmlString.append("%s</%s>%s" % (prefix, elementName, newline))
1174 else:
1175 if hasattr(obj, "__xmlcdatacontent__"):
1176 cdataAttr = obj.__xmlcdatacontent__
1177 cdataContent = obj.__dict__[cdataAttr]
1178 xmlString.append("><![CDATA[%s]]></%s>%s" % (cdataContent, elementName, newline))
1179 else:
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)
1184 self.popNSStack()
1185 return xmlString
1186
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"]}
1195
1196 def setPerson(self):
1197 self.firstName = "Albert"
1198 self.lastName = "Camus"
1199 self.addressLine1 = "23 Absurd St."
1200 self.city = "Ennui"
1201 self.state = "MO"
1202 self.zip = "54321"
1203 self._phoneNumber = "808-303-2323"
1204 self.favoriteWords = ["angst", "ennui", "existence"]
1205 self.phobias = ["war", "tuberculosis", "cars"]
1206 self.weight = 150
1207 self.fabulousness = "tres tres"
1208 self.nonSmoker = False
1209
1210 if isMain(__name__):
1211 p1 = MarshallerPerson()
1212 p1.setPerson()
1213 xmlP1 = marshal(p1, prettyPrint=True, encoding="utf-8")
1214 print "\n########################"
1215 print "# testPerson test case #"
1216 print "########################"
1217 print xmlP1
1218 p2 = unmarshal(xmlP1)
1219 xmlP2 = marshal(p2, prettyPrint=True, encoding="utf-8")
1220 if xmlP1 == xmlP2:
1221 print "Success: repeated marshalling yields identical results"
1222 else:
1223 print "Failure: repeated marshalling yields different results"
1224 print xmlP2