def parse(cls, definitions, xmlelement): name = qname_attr(xmlelement, "name", definitions.target_namespace) port_name = qname_attr(xmlelement, "type", definitions.target_namespace) obj = cls(definitions.wsdl, name, port_name) for node in xmlelement.findall("wsdl:operation", namespaces=NSMAP): operation = HttpOperation.parse(definitions, node, obj) obj._operation_add(operation) return obj
def visit_restriction_simple_content(self, node, parent): """ Definition:: <restriction base = QName id = ID {any attributes with non-schema Namespace}...> Content: (annotation?, (simpleType?, ( minExclusive | minInclusive | maxExclusive | maxInclusive | totalDigits |fractionDigits | length | minLength | maxLength | enumeration | whiteSpace | pattern)* )?, ((attribute | attributeGroup)*, anyAttribute?)) </restriction> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ base_name = qname_attr(node, "base") base_type = self._get_type(base_name) return base_type, []
def visit_extension_complex_content(self, node, parent): """ Definition:: <extension base = QName id = ID {any attributes with non-schema Namespace}...> Content: (annotation?, ( (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))) </extension> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ base_name = qname_attr(node, "base") base_type = self._get_type(base_name) annotation, children = self._pop_annotation(list(node)) element = None attributes = [] if children: child = children[0] if child.tag in (tags.group, tags.all, tags.choice, tags.sequence): children.pop(0) element = self.process(child, node) attributes = self._process_attributes(node, children) return base_type, element, attributes
def visit_attribute_group(self, node, parent): """ Definition:: <attributeGroup id = ID name = NCName ref = QName {any attributes with non-schema Namespace...}> Content: (annotation?), ((attribute | attributeGroup)*, anyAttribute?)) </attributeGroup> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ ref = self.process_reference(node) if ref: return ref qname = qname_attr(node, "name", self.document._target_namespace) annotation, children = self._pop_annotation(list(node)) attributes = self._process_attributes(node, children) attribute_group = xsd_elements.AttributeGroup(qname, attributes) self.register_attribute_group(qname, attribute_group)
def visit_restriction_simple_type(self, node, parent): """ Definition:: <restriction base = QName id = ID {any attributes with non-schema Namespace}...> Content: (annotation?, (simpleType?, ( minExclusive | minInclusive | maxExclusive | maxInclusive | totalDigits |fractionDigits | length | minLength | maxLength | enumeration | whiteSpace | pattern)*)) </restriction> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ base_name = qname_attr(node, "base") if base_name: return self._get_type(base_name) annotation, children = self._pop_annotation(list(node)) if children[0].tag == tags.simpleType: return self.visit_simple_type(children[0], node)
def visit_list(self, node, parent): """ Definition:: <list id = ID itemType = QName {any attributes with non-schema Namespace}...> Content: (annotation?, (simpleType?)) </list> The use of the simpleType element child and the itemType attribute is mutually exclusive. :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ item_type = qname_attr(node, "itemType") if item_type: sub_type = self._get_type(item_type.text) else: subnodes = list(node) child = subnodes[-1] # skip annotation sub_type = self.visit_simple_type(child, node) return xsd_types.ListType(sub_type)
def parse(self, xmlelement, schema, allow_none=False, context=None): """Process the given xmlelement. If it has an xsi:type attribute then use that for further processing. This should only be done for subtypes of the defined type but for now we just accept everything. This is the entrypoint for parsing an xml document. :param xmlelement: The XML element to parse :type xmlelements: lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param allow_none: Allow none :type allow_none: bool :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :return: dict or None """ context = context or XmlParserContext() instance_type = qname_attr(xmlelement, xsi_ns("type")) xsd_type = None if instance_type: xsd_type = schema.get_type(instance_type, fail_silently=True) xsd_type = xsd_type or self.type return xsd_type.parse_xmlelement( xmlelement, schema, allow_none=allow_none, context=context, schema_type=self.type, )
def parse(self, xmlelement, schema, context=None): if self.process_contents == "skip": return xmlelement # If a schema was passed inline then check for a matching one qname = etree.QName(xmlelement.tag) if context and context.schemas: for context_schema in context.schemas: if context_schema.documents.has_schema_document_for_ns(qname.namespace): schema = context_schema break else: # Try to parse the any result by iterating all the schemas for context_schema in context.schemas: try: data = context_schema.deserialize(list(xmlelement)[0]) return data except LookupError: continue # Lookup type via xsi:type attribute xsd_type = qname_attr(xmlelement, xsi_ns("type")) if xsd_type is not None: xsd_type = schema.get_type(xsd_type) return xsd_type.parse_xmlelement(xmlelement, schema, context=context) # Check if a restrict is used if self.restrict: return self.restrict.parse_xmlelement(xmlelement, schema, context=context) try: element = schema.get_element(xmlelement.tag) return element.parse(xmlelement, schema, context=context) except (exceptions.NamespaceError, exceptions.LookupError): return xmlelement
def parse(cls, definitions, xmlelement): """ Definition:: <wsdl:binding name="nmtoken" type="qname"> * <-- extensibility element (1) --> * <wsdl:operation name="nmtoken"> * <-- extensibility element (2) --> * <wsdl:input name="nmtoken"? > ? <-- extensibility element (3) --> </wsdl:input> <wsdl:output name="nmtoken"? > ? <-- extensibility element (4) --> * </wsdl:output> <wsdl:fault name="nmtoken"> * <-- extensibility element (5) --> * </wsdl:fault> </wsdl:operation> </wsdl:binding> """ name = qname_attr(xmlelement, "name", definitions.target_namespace) port_name = qname_attr(xmlelement, "type", definitions.target_namespace) # The soap:binding element contains the transport method and # default style attribute for the operations. soap_node = xmlelement.find("soap:binding", namespaces=cls.nsmap) transport = soap_node.get("transport") supported_transports = [ "http://schemas.xmlsoap.org/soap/http", "http://www.w3.org/2003/05/soap/bindings/HTTP/", ] if transport not in supported_transports: raise NotImplementedError( "The binding transport %s is not supported (only soap/http)" % (transport) ) default_style = soap_node.get("style", "document") obj = cls(definitions.wsdl, name, port_name, transport, default_style) for node in xmlelement.findall("wsdl:operation", namespaces=cls.nsmap): operation = SoapOperation.parse(definitions, node, obj, nsmap=cls.nsmap) obj._operation_add(operation) return obj
def process_ref_attribute(self, node, array_type=None): ref = qname_attr(node, "ref") if ref: ref = self._create_qname(ref) # Some wsdl's reference to xs:schema, we ignore that for now. It # might be better in the future to process the actual schema file # so that it is handled correctly if ref.namespace == "http://www.w3.org/2001/XMLSchema": return return xsd_elements.RefAttribute( node.tag, ref, self.schema, array_type=array_type )
def process_reference(self, node, **kwargs): ref = qname_attr(node, "ref") if not ref: return ref = self._create_qname(ref) if node.tag == tags.element: cls = xsd_elements.RefElement elif node.tag == tags.attribute: cls = xsd_elements.RefAttribute elif node.tag == tags.group: cls = xsd_elements.RefGroup elif node.tag == tags.attributeGroup: cls = xsd_elements.RefAttributeGroup return cls(node.tag, ref, self.schema, **kwargs)
def visit_extension_simple_content(self, node, parent): """ Definition:: <extension base = QName id = ID {any attributes with non-schema Namespace}...> Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?)) </extension> """ base_name = qname_attr(node, "base") base_type = self._get_type(base_name) annotation, children = self._pop_annotation(list(node)) attributes = self._process_attributes(node, children) return base_type, attributes
def visit_group(self, node, parent): """Groups a set of element declarations so that they can be incorporated as a group into complex type definitions. Definition:: <group name= NCName id = ID maxOccurs = (nonNegativeInteger | unbounded) : 1 minOccurs = nonNegativeInteger : 1 name = NCName ref = QName {any attributes with non-schema Namespace}...> Content: (annotation?, (all | choice | sequence)) </group> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ min_occurs, max_occurs = _process_occurs_attrs(node) result = self.process_reference( node, min_occurs=min_occurs, max_occurs=max_occurs ) if result: return result qname = qname_attr(node, "name", self.document._target_namespace) # There should be only max nodes, first node (annotation) is irrelevant annotation, children = self._pop_annotation(list(node)) child = children[0] item = self.process(child, parent) elm = xsd_elements.Group(name=qname, child=item) if parent.tag == tags.schema: self.register_group(qname, elm) return elm
def visit_attribute(self, node, parent): """Declares an attribute. Definition:: <attribute default = string fixed = string form = (qualified | unqualified) id = ID name = NCName ref = QName type = QName use = (optional | prohibited | required): optional {any attributes with non-schema Namespace...}> Content: (annotation?, (simpleType?)) </attribute> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ is_global = parent.tag == tags.schema # Check of wsdl:arayType array_type = node.get("{http://schemas.xmlsoap.org/wsdl/}arrayType") if array_type: match = re.match(r"([^\[]+)", array_type) if match: array_type = match.groups()[0] qname = as_qname(array_type, node.nsmap) array_type = UnresolvedType(qname, self.schema) # If the elment has a ref attribute then all other attributes cannot # be present. Short circuit that here. # Ref is prohibited on global elements (parent = schema) if not is_global: result = self.process_ref_attribute(node, array_type=array_type) if result: return result attribute_form = node.get("form", self.document._attribute_form) if attribute_form == "qualified" or is_global: name = qname_attr(node, "name", self.document._target_namespace) else: name = etree.QName(node.get("name")) annotation, items = self._pop_annotation(list(node)) if items: xsd_type = self.visit_simple_type(items[0], node) else: node_type = qname_attr(node, "type") if node_type: xsd_type = self._get_type(node_type) else: xsd_type = xsd_types.AnyType() # TODO: We ignore 'prohobited' for now required = node.get("use") == "required" default = node.get("default") attr = xsd_elements.Attribute( name, type_=xsd_type, default=default, required=required ) # Only register global elements if is_global: self.register_attribute(name, attr) return attr
def visit_element(self, node, parent): """ Definition:: <element abstract = Boolean : false block = (#all | List of (extension | restriction | substitution)) default = string final = (#all | List of (extension | restriction)) fixed = string form = (qualified | unqualified) id = ID maxOccurs = (nonNegativeInteger | unbounded) : 1 minOccurs = nonNegativeInteger : 1 name = NCName nillable = Boolean : false ref = QName substitutionGroup = QName type = QName {any attributes with non-schema Namespace}...> Content: (annotation?, ( (simpleType | complexType)?, (unique | key | keyref)*)) </element> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ is_global = parent.tag == tags.schema # minOccurs / maxOccurs are not allowed on global elements if not is_global: min_occurs, max_occurs = _process_occurs_attrs(node) else: max_occurs = 1 min_occurs = 1 # If the element has a ref attribute then all other attributes cannot # be present. Short circuit that here. # Ref is prohibited on global elements (parent = schema) if not is_global: # Naive workaround to mark fields which are part of a choice element # as optional if parent.tag == tags.choice: min_occurs = 0 result = self.process_reference( node, min_occurs=min_occurs, max_occurs=max_occurs ) if result: return result element_form = node.get("form", self.document._element_form) if element_form == "qualified" or is_global: qname = qname_attr(node, "name", self.document._target_namespace) else: qname = etree.QName(node.get("name").strip()) children = list(node) xsd_type = None if children: value = None for child in children: if child.tag == tags.annotation: continue elif child.tag in (tags.simpleType, tags.complexType): assert not value xsd_type = self.process(child, node) if not xsd_type: node_type = qname_attr(node, "type") if node_type: xsd_type = self._get_type(node_type.text) else: xsd_type = xsd_types.AnyType() nillable = node.get("nillable") == "true" default = node.get("default") element = xsd_elements.Element( name=qname, type_=xsd_type, min_occurs=min_occurs, max_occurs=max_occurs, nillable=nillable, default=default, is_global=is_global, ) # Only register global elements if is_global: self.register_element(qname, element) return element
def parse_xmlelement(self, xmlelement, schema=None, allow_none=True, context=None, schema_type=None): """Consume matching xmlelements and call parse() on each :param xmlelement: XML element objects :type xmlelement: lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param allow_none: Allow none :type allow_none: bool :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :param schema_type: The original type (not overriden via xsi:type) :type schema_type: zeep.xsd.types.base.Type :rtype: dict or None """ xsi_type = qname_attr(xmlelement, xsi_ns("type")) xsi_nil = xmlelement.get(xsi_ns("nil")) children = list(xmlelement) # Handle xsi:nil attribute if xsi_nil == "true": return None # Check if a xsi:type is defined and try to parse the xml according # to that type. if xsi_type and schema: xsd_type = schema.get_type(xsi_type, fail_silently=True) # If we were unable to resolve a type for the xsi:type (due to # buggy soap servers) then we just return the text or lxml element. if not xsd_type: logger.debug( "Unable to resolve type for %r, returning raw data", xsi_type.text) if xmlelement.text: return xmlelement.text return children # If the xsd_type is xsd:anyType then we will recurs so ignore # that. if isinstance(xsd_type, self.__class__): return xmlelement.text or None return xsd_type.parse_xmlelement(xmlelement, schema, context=context) # If no xsi:type is set and the element has children then there is # not much we can do. Just return the children elif children: return children elif xmlelement.text is not None: return self.pythonvalue(xmlelement.text) return None