You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
7.4 KiB
216 lines
7.4 KiB
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the (LGPL) GNU Lesser General Public License as
|
|
# published by the Free Software Foundation; either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Library Lesser General Public License for more details at
|
|
# ( http://www.gnu.org/licenses/lgpl.html ).
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
# written by: Jeff Ortel ( jortel@redhat.com )
|
|
|
|
"""
|
|
Provides base classes for XML->object I{unmarshalling}.
|
|
"""
|
|
|
|
from logging import getLogger
|
|
from suds import *
|
|
from suds.umx import *
|
|
from suds.umx.attrlist import AttrList
|
|
from suds.sax.text import Text
|
|
from suds.sudsobject import Factory, merge
|
|
|
|
|
|
log = getLogger(__name__)
|
|
|
|
reserved = { 'class':'cls', 'def':'dfn', }
|
|
|
|
class Core:
|
|
"""
|
|
The abstract XML I{node} unmarshaller. This class provides the
|
|
I{core} unmarshalling functionality.
|
|
"""
|
|
|
|
def process(self, content):
|
|
"""
|
|
Process an object graph representation of the xml I{node}.
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
@return: A suds object.
|
|
@rtype: L{Object}
|
|
"""
|
|
self.reset()
|
|
return self.append(content)
|
|
|
|
def append(self, content):
|
|
"""
|
|
Process the specified node and convert the XML document into
|
|
a I{suds} L{object}.
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
@return: A I{append-result} tuple as: (L{Object}, I{value})
|
|
@rtype: I{append-result}
|
|
@note: This is not the proper entry point.
|
|
@see: L{process()}
|
|
"""
|
|
self.start(content)
|
|
self.append_attributes(content)
|
|
self.append_children(content)
|
|
self.append_text(content)
|
|
self.end(content)
|
|
return self.postprocess(content)
|
|
|
|
def postprocess(self, content):
|
|
"""
|
|
Perform final processing of the resulting data structure as follows:
|
|
- Mixed values (children and text) will have a result of the I{content.node}.
|
|
- Simi-simple values (attributes, no-children and text) will have a result of a
|
|
property object.
|
|
- Simple values (no-attributes, no-children with text nodes) will have a string
|
|
result equal to the value of the content.node.getText().
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
@return: The post-processed result.
|
|
@rtype: I{any}
|
|
"""
|
|
node = content.node
|
|
if len(node.children) and node.hasText():
|
|
return node
|
|
attributes = AttrList(node.attributes)
|
|
if attributes.rlen() and \
|
|
not len(node.children) and \
|
|
node.hasText():
|
|
p = Factory.property(node.name, node.getText())
|
|
return merge(content.data, p)
|
|
if len(content.data):
|
|
return content.data
|
|
lang = attributes.lang()
|
|
if content.node.isnil():
|
|
return None
|
|
if not len(node.children) and content.text is None:
|
|
if self.nillable(content):
|
|
return None
|
|
else:
|
|
return Text('', lang=lang)
|
|
if isinstance(content.text, basestring):
|
|
return Text(content.text, lang=lang)
|
|
else:
|
|
return content.text
|
|
|
|
def append_attributes(self, content):
|
|
"""
|
|
Append attribute nodes into L{Content.data}.
|
|
Attributes in the I{schema} or I{xml} namespaces are skipped.
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
"""
|
|
attributes = AttrList(content.node.attributes)
|
|
for attr in attributes.real():
|
|
name = attr.name
|
|
value = attr.value
|
|
self.append_attribute(name, value, content)
|
|
|
|
def append_attribute(self, name, value, content):
|
|
"""
|
|
Append an attribute name/value into L{Content.data}.
|
|
@param name: The attribute name
|
|
@type name: basestring
|
|
@param value: The attribute's value
|
|
@type value: basestring
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
"""
|
|
key = name
|
|
key = '_%s' % reserved.get(key, key)
|
|
setattr(content.data, key, value)
|
|
|
|
def append_children(self, content):
|
|
"""
|
|
Append child nodes into L{Content.data}
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
"""
|
|
for child in content.node:
|
|
cont = Content(child)
|
|
cval = self.append(cont)
|
|
key = reserved.get(child.name, child.name)
|
|
if key in content.data:
|
|
v = getattr(content.data, key)
|
|
if isinstance(v, list):
|
|
v.append(cval)
|
|
else:
|
|
setattr(content.data, key, [v, cval])
|
|
continue
|
|
if self.unbounded(cont):
|
|
if cval is None:
|
|
setattr(content.data, key, [])
|
|
else:
|
|
setattr(content.data, key, [cval,])
|
|
else:
|
|
setattr(content.data, key, cval)
|
|
|
|
def append_text(self, content):
|
|
"""
|
|
Append text nodes into L{Content.data}
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
"""
|
|
if content.node.hasText():
|
|
content.text = content.node.getText()
|
|
|
|
def reset(self):
|
|
pass
|
|
|
|
def start(self, content):
|
|
"""
|
|
Processing on I{node} has started. Build and return
|
|
the proper object.
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
@return: A subclass of Object.
|
|
@rtype: L{Object}
|
|
"""
|
|
content.data = Factory.object(content.node.name)
|
|
|
|
def end(self, content):
|
|
"""
|
|
Processing on I{node} has ended.
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
"""
|
|
pass
|
|
|
|
def bounded(self, content):
|
|
"""
|
|
Get whether the content is bounded (not a list).
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
@return: True if bounded, else False
|
|
@rtype: boolean
|
|
'"""
|
|
return ( not self.unbounded(content) )
|
|
|
|
def unbounded(self, content):
|
|
"""
|
|
Get whether the object is unbounded (a list).
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
@return: True if unbounded, else False
|
|
@rtype: boolean
|
|
'"""
|
|
return False
|
|
|
|
def nillable(self, content):
|
|
"""
|
|
Get whether the object is nillable.
|
|
@param content: The current content being unmarshalled.
|
|
@type content: L{Content}
|
|
@return: True if nillable, else False
|
|
@rtype: boolean
|
|
'"""
|
|
return False
|