def envelope(self, headers, body, bodytag=None): ''' Build a SOAP envelope given headers and body. ''' env = ET.Element(ns.expand('soapenv:Envelope', self.nsmap), nsmap=self.nsmap) envheader = ET.Element(ns.expand('soapenv:Header', self.nsmap)) envbody = ET.Element(ns.expand('soapenv:Body', self.nsmap)) env.append(envheader) env.append(envbody) for header in headers: if not ET.iselement(header): if hasattr(header, '__xml__'): header = header.__xml__() else: raise Exception('Cannott create SOAP:header') envheader.append(header) if body is not None: if not ET.iselement(body): if hasattr(body, '__xml__'): body = body.__xml__(tag=bodytag) else: raise Exception('Cannot create SOAP:body') envbody.append(body) return env
def __xml__(self, tag=None, node=None, nsmap=None, ignore=[]): ''' Convert a DynamicObject to XML elements @type tag: str @param tag: Optional. The name of the tag of the root node. @type node: L{ElementTree.Element} @param node: Optional. A root node to populate. @type nsmap: dict @param nsmap: Optional. A dictionary of namespace prefixes and namespaces to use. @type ignore: list @param ignore: Optional. A list of fields to ignore during serialization to XML. @rtype: L{ElementTree.Element} @return: An XML Element representing this object. ''' at = self.__attrchar__ prop = self.__property__ if tag is None: tag = self.__class__.__name__ tag = self.__nsx__(tag) if node is None: node = ET.Element(tag, nsmap=nsmap) for key, value in self: if key in ignore: continue if key[0] == at: node.set(key[1:], unicode(value)) continue name = self.__nsx__(key) if key == prop: node.text = unicode(value) continue if not isinstance(value, (list, tuple)): value = [value] for v in value: if isinstance(v, DynamicObject): node.append(v.__xml__(key)) elif ET.iselement(v): node.append(v) else: n = ET.Element(name) if v is not None: n.text = unicode(v) node.append(n) return node
def load(self, schema, force=False, fragment=False, pathinfo='', basecls=None): ''' Load and pre-process an XML schema. @type schema: L{ElementTree.Element} or URL @param schema: A schema to load. @type force: bool @param force: Optional. Reload an already loaded schema. @type fragment: bool @param fragment: Optional. The schema is actually a fragment of a an already-loaded schema and should be integrated with it. @type pathinfo: str @param pathinfo: Optional. A URL to help with loading the schema. Usually used by SchemaLoader to process xs:import directives. @rtype: str @return: The targetNamespace of the loaded schema ''' # Is schema already parsed if ET.iselement(schema): root = schema else: schema = urljoin(pathinfo, schema) root = ET.parse(u2.urlopen(schema)).getroot() # Get the target namespace. Exit early if we already know this schema. targetNamespace = root.get('targetNamespace') if targetNamespace in self.schemas and not (force or fragment): return targetNamespace # Add a new entry to our dictionary if not fragment: self.schemas[targetNamespace] = { 'root': root, 'types': {}, 'elements': {}, 'groups': {}, 'validator': None, 'basecls': basecls } # Update our "all namespaces" dictionary and get references to the # various subdictionaries we'll need self.allns.update(root.nsmap) self.revns.update( (v, k) for k, v in root.nsmap.items() if k not in (None, 'tns')) types = self.schemas[targetNamespace]['types'] elements = self.schemas[targetNamespace]['elements'] groups = self.schemas[targetNamespace]['groups'] # Process includes includes = [] while True: # Get the list of includes inclist = root.findall(ns.expand('xs:include')) if not inclist: break for el in inclist: # remove it from the document root.remove(el) # Get the schemaLocation and compute the URL location = el.get('schemaLocation') if location in includes: # skip if we've processed this schema continue includes.append(location) url = urljoin(pathinfo, location) # Parse the XML and append it to the root document # We probably *should* include it into the place where the # xs:include node was, but for now, punt and append it # to the end of the document inc = ET.parse(u2.urlopen(url)).getroot() root.extend(inc) # Process imports for el in root.findall(ns.expand('xs:import')): location = el.get('schemaLocation') if location: self.load(location, pathinfo=pathinfo) # Find all first-level tags we care about and reference them # in the types/elements/groups dictionaries for el in root.findall(ns.expand('xs:complexType')): types[el.get('name')] = el for el in root.findall(ns.expand('xs:simpleType')): types[el.get('name')] = el for el in root.findall(ns.expand('xs:element')): elements[el.get('name')] = el for el in root.findall(ns.expand('xs:group')): groups[el.get('name')] = el # If this is a schema fragment, integrate it into the # original schema element tree in memory if fragment: realroot = self.schemas[targetNamespace]['root'] nsmap = dict(realroot.nsmap) nsmap.update(root.nsmap) attrib = dict(realroot.attrib) attrib.update(root.attrib) newroot = ET.Element(realroot.tag, attrib=attrib, nsmap=nsmap) newroot.text = realroot.text newroot.extend(realroot.getchildren()) newroot.extend(root.getchildren()) self.schemas[targetNamespace]['root'] = newroot return targetNamespace
def __xml__(self, tag=None, node=None, nsmap=None): lat = len(self.__attrchar__) done = [] extra = False if tag is None: tag = self.__class__.__name__ tag = self.__nsx__(tag) if node is None: node = ET.Element(tag, nsmap=nsmap) # Construct reverse namespace map for doing xsi:type rmap = dict((v, k) for k, v in node.nsmap.items() if k is not None) # Iterate over the template to construct the XML representation. for (name, type, default, minmax, flags) in self.__template__: # If this field is an "xs:any" node, note it for later and skip if (flags & ANY): extra = True continue # If the field is choice/optional and doesn't exist, skip it if ((flags & CHOICE) or minmax[0] == 0) and name not in self: continue # Get the value value = self[name] done.append(name) if value is None and minmax[0] == 0 and not (flags & XSINIL): # Skip fields that are none and don't have to exist, as long # as they aren't nillable continue elif (flags & ATTRIBUTE): # Handle attributes. Skip attributes that are optional value = converter(type).tostr(value) if value is None: value = '' if value is not None or minmax[0]: node.set(self.__nsx__(name[lat:], flags & QUALIFIED), value) continue elif (flags & PROPERTY): # Property node.text = converter(type).tostr(value) continue qname = self.__nsx__(name) if not isinstance(value, (list, tuple)): value = [value] for v in value: if v is None and (flags & XSINIL): # Nil node n = ET.Element(qname, xsi_nil_true) node.append(n) elif ET.iselement(v): # User supplied XML Elements, so just add them node.append(v) elif type.startswith('xs:'): # Primitive type n = ET.Element(qname) n.text = converter(type).tostr(v) node.append(n) elif flags & SIMPLE: # Primitive type type = self.__builder__.factory(type).__simple__ n = ET.Element(qname) n.text = converter(type).tostr(v) node.append(n) elif isinstance(v, DynamicObject): # Dynamic object or subclass, so marshall and append n = v.__xml__(name) if type != v.__class__.__name__: (namespace, datatype) = ns.split(v.__class__.__name__) n.set(xsi_type, '%s:%s' % (rmap[namespace], datatype)) node.append(n) elif v == '': # Carry-over for dealing with SUDS bug pass else: if not self.__relax__: raise TypeError('Unknown type', name, type) # If there was an xs:any node, fall back to the schemaless marshaller # in the base class if extra: DynamicObject.__xml__(self, tag, node, ignore=done) return node