def parse_xmlelement(self, xmlelement, schema, allow_none=True, context=None): """Consume matching xmlelements and call parse() on each""" # If this is an empty complexType (<xsd:complexType name="x"/>) if not self.attributes and not self.elements: return None attributes = xmlelement.attrib init_kwargs = OrderedDict() # If this complexType extends a simpleType then we have no nested # elements. Parse it directly via the type object. This is the case # for xsd:simpleContent if isinstance(self._element, Element) and isinstance( self._element.type, SimpleType): name, element = self.elements_nested[0] init_kwargs[name] = element.type.parse_xmlelement(xmlelement, schema, name, context=context) else: elements = deque(xmlelement.iterchildren()) if allow_none and len(elements) == 0 and len(attributes) == 0: return # Parse elements. These are always indicator elements (all, choice, # group, sequence) for name, element in self.elements_nested: try: result = element.parse_xmlelements(elements, schema, name, context=context) if result: init_kwargs.update(result) except UnexpectedElementError as exc: raise XMLParseError(exc.message) # Check if all children are consumed (parsed) if elements: raise XMLParseError("Unexpected element %r" % elements[0].tag) # Parse attributes if attributes: attributes = copy.copy(attributes) for name, attribute in self.attributes: if attribute.name: if attribute.qname.text in attributes: value = attributes.pop(attribute.qname.text) init_kwargs[name] = attribute.parse(value) else: init_kwargs[name] = attribute.parse(attributes) return self(**init_kwargs)
def as_qname(value: str, nsmap, target_namespace=None) -> etree.QName: """Convert the given value to a QName""" value = value.strip() # some xsd's contain leading/trailing spaces if ":" in value: prefix, local = value.split(":") # The xml: prefix is always bound to the XML namespace, see # https://www.w3.org/TR/xml-names/ if prefix == "xml": namespace = "http://www.w3.org/XML/1998/namespace" else: namespace = nsmap.get(prefix) if not namespace: raise XMLParseError("No namespace defined for %r (%r)" % (prefix, value)) # Workaround for https://github.com/mvantellingen/python-zeep/issues/349 if not local: return etree.QName(XSD, "anyType") return etree.QName(namespace, local) if target_namespace: return etree.QName(target_namespace, value) if nsmap.get(None): return etree.QName(nsmap[None], value) return etree.QName(value)
def as_qname(value, nsmap, target_namespace=None): """Convert the given value to a QName""" if ':' in value: prefix, local = value.split(':') # The xml: prefix is always bound to the XML namespace, see # https://www.w3.org/TR/xml-names/ if prefix == 'xml': namespace = 'http://www.w3.org/XML/1998/namespace' else: namespace = nsmap.get(prefix) if not namespace: raise XMLParseError("No namespace defined for %r" % prefix) # Workaround for https://github.com/mvantellingen/python-zeep/issues/349 if not local: return etree.QName(XSD, 'anyType') return etree.QName(namespace, local) if target_namespace: return etree.QName(target_namespace, value) if nsmap.get(None): return etree.QName(nsmap[None], value) return etree.QName(value)
def as_qname(value, nsmap, target_namespace=None): """Convert the given value to a QName""" value = value.strip() # some xsd's contain leading/trailing spaces if ':' in value: prefix, local = value.split(':') # The xml: prefix is always bound to the XML namespace, see # https://www.w3.org/TR/xml-names/ if prefix == 'xml': namespace = 'http://www.w3.org/XML/1998/namespace' else: namespace = nsmap.get(prefix) if not namespace: raise XMLParseError("No namespace defined for %r (%r)" % (prefix, value)) # Workaround for https://github.com/mvantellingen/python-zeep/issues/349 if not local: return etree.QName(XSD, 'anyType') # Workaround a idiotice do b1ws devolver <env:Value>env:-1013</env:Value> # O -1013 dá erro. if local[0] in "-0123456789": return etree.QName(namespace, "_" + local) return etree.QName(namespace, local) if target_namespace: return etree.QName(target_namespace, value) if nsmap.get(None): return etree.QName(nsmap[None], value) return etree.QName(value)
def _process_attributes(self, node, items): attributes = [] for child in items: attribute = self.process(child, node) if child.tag in (tags.attribute, tags.attributeGroup, tags.anyAttribute): attributes.append(attribute) else: raise XMLParseError("Unexpected tag: %s" % child.tag) return attributes
def _process_attributes(self, node, items): attributes = [] for child in items: if child.tag in (tags.attribute, tags.attributeGroup, tags.anyAttribute): attribute = self.process(child, node) attributes.append(attribute) else: raise XMLParseError("Unexpected tag `%s`" % (child.tag), filename=self.document._location, sourceline=node.sourceline) return attributes
def as_qname(value, nsmap, target_namespace): """Convert the given value to a QName""" if ':' in value: prefix, local = value.split(':') namespace = nsmap.get(prefix) if not namespace: raise XMLParseError("No namespace defined for %r" % prefix) # Workaround for https://github.com/mvantellingen/python-zeep/issues/349 if not local: return etree.QName(XSD, 'anyType') return etree.QName(namespace, local) if target_namespace: return etree.QName(target_namespace, value) if nsmap.get(None): return etree.QName(nsmap[None], value) return etree.QName(value)
def visit_import(self, node, parent): """ Definition:: <import id = ID namespace = anyURI schemaLocation = anyURI {any attributes with non-schema Namespace}...> Content: (annotation?) </import> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ schema_node = None namespace = node.get("namespace") location = node.get("schemaLocation") if location: location = normalize_location(self.schema.settings, location, self.document._base_url) if not namespace and not self.document._target_namespace: raise XMLParseError( "The attribute 'namespace' must be existent if the " "importing schema has no target namespace.", filename=self.document.location, sourceline=node.sourceline, ) # We found an empty <import/> statement, this needs to trigger 4.1.2 # from https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#src-resolve # for QName resolving. # In essence this means we will resolve QNames without a namespace to no # namespace instead of the target namespace. # The following code snippet works because imports have to occur before we # visit elements. if not namespace and not location: self.document._has_empty_import = True # Check if the schema is already imported before based on the # namespace. Schema's without namespace are registered as 'None' document = self.schema.documents.get_by_namespace_and_location( namespace, location) if document: logger.debug("Returning existing schema: %r", location) self.register_import(namespace, document) return document # Hardcode the mapping between the xml namespace and the xsd for now. # This seems to fix issues with exchange wsdl's, see #220 if not location and namespace == "http://www.w3.org/XML/1998/namespace": location = "https://www.w3.org/2001/xml.xsd" # Silently ignore import statements which we can't resolve via the # namespace and doesn't have a schemaLocation attribute. if not location: logger.debug( "Ignoring import statement for namespace %r " + "(missing schemaLocation)", namespace, ) return # Load the XML schema_node = self._retrieve_data(location, base_url=self.document._location) # Check if the xsd:import namespace matches the targetNamespace. If # the xsd:import statement didn't specify a namespace then make sure # that the targetNamespace wasn't declared by another schema yet. schema_tns = schema_node.get("targetNamespace") if namespace and schema_tns and namespace != schema_tns: raise XMLParseError( ("The namespace defined on the xsd:import doesn't match the " "imported targetNamespace located at %r ") % (location), filename=self.document._location, sourceline=node.sourceline, ) # If the imported schema doesn't define a target namespace and the # node doesn't specify it either then inherit the existing target # namespace. elif not schema_tns and not namespace: namespace = self.document._target_namespace schema = self.schema.create_new_document(schema_node, location, target_namespace=namespace) self.register_import(namespace, schema) return schema
def _create_error(self, message, node): return XMLParseError(message, filename=self.document._location, sourceline=node.sourceline)
def visit_import(self, node, parent): """ Definition:: <import id = ID namespace = anyURI schemaLocation = anyURI {any attributes with non-schema Namespace}...> Content: (annotation?) </import> :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ schema_node = None namespace = node.get('namespace') location = node.get('schemaLocation') if location: location = absolute_location(location, self.document._base_url) if not namespace and not self.document._target_namespace: raise XMLParseError( "The attribute 'namespace' must be existent if the " "importing schema has no target namespace.", filename=self._document.location, sourceline=node.sourceline) # Check if the schema is already imported before based on the # namespace. Schema's without namespace are registered as 'None' document = self.schema.documents.get_by_namespace_and_location( namespace, location) if document: logger.debug("Returning existing schema: %r", location) self.register_import(namespace, document) return document # Hardcode the mapping between the xml namespace and the xsd for now. # This seems to fix issues with exchange wsdl's, see #220 if not location and namespace == 'http://www.w3.org/XML/1998/namespace': location = 'https://www.w3.org/2001/xml.xsd' # Silently ignore import statements which we can't resolve via the # namespace and doesn't have a schemaLocation attribute. if not location: logger.debug( "Ignoring import statement for namespace %r " + "(missing schemaLocation)", namespace) return # Load the XML schema_node = load_external(location, self.schema._transport, settings=self.schema.settings) # Check if the xsd:import namespace matches the targetNamespace. If # the xsd:import statement didn't specify a namespace then make sure # that the targetNamespace wasn't declared by another schema yet. schema_tns = schema_node.get('targetNamespace') if namespace and schema_tns and namespace != schema_tns: raise XMLParseError( ("The namespace defined on the xsd:import doesn't match the " "imported targetNamespace located at %r ") % (location), filename=self.document._location, sourceline=node.sourceline) schema = self.schema.create_new_document(schema_node, location) self.register_import(namespace, schema) return schema
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 """ # If this is an empty complexType (<xsd:complexType name="x"/>) if not self.attributes and not self.elements: return None attributes = xmlelement.attrib init_kwargs = OrderedDict() # If this complexType extends a simpleType then we have no nested # elements. Parse it directly via the type object. This is the case # for xsd:simpleContent if isinstance(self._element, Element) and isinstance( self._element.type, AnySimpleType): name, element = self.elements_nested[0] init_kwargs[name] = element.type.parse_xmlelement(xmlelement, schema, name, context=context) else: elements = deque(xmlelement.iterchildren()) if allow_none and len(elements) == 0 and len(attributes) == 0: return # Parse elements. These are always indicator elements (all, choice, # group, sequence) assert len(self.elements_nested) < 2 for name, element in self.elements_nested: try: result = element.parse_xmlelements(elements, schema, name, context=context) if result: init_kwargs.update(result) except UnexpectedElementError as exc: raise XMLParseError(exc.message) # Check if all children are consumed (parsed) if elements: if schema.strict: raise XMLParseError("Unexpected element %r" % elements[0].tag) else: init_kwargs['_raw_elements'] = elements # Parse attributes if attributes: attributes = copy.copy(attributes) for name, attribute in self.attributes: if attribute.name: if attribute.qname.text in attributes: value = attributes.pop(attribute.qname.text) init_kwargs[name] = attribute.parse(value) else: init_kwargs[name] = attribute.parse(attributes) value = self._value_class(**init_kwargs) schema_type = schema_type or self if schema_type and getattr(schema_type, '_array_type', None): value = schema_type._array_class.from_value_object(value) return value
def visit_import(self, node, parent): """ <import id = ID namespace = anyURI schemaLocation = anyURI {any attributes with non-schema Namespace}...> Content: (annotation?) </import> """ schema_node = None namespace = node.get('namespace') location = node.get('schemaLocation') if location: location = absolute_location(location, self.schema._base_url) if not namespace and not self.schema._target_namespace: raise XMLParseError( "The attribute 'namespace' must be existent if the " "importing schema has no target namespace.") # Check if the schema is already imported before based on the # namespace. Schema's without namespace are registered as 'None' schema = self.parser_context.schema_objects.get(namespace) if schema: if location and schema._location != location: # Use same warning message as libxml2 message = ("Skipping import of schema located at %r " + "for the namespace %r, since the namespace was " + "already imported with the schema located at %r" ) % (location, namespace or '(null)', schema._location) warnings.warn(message, ZeepWarning, stacklevel=6) return logger.debug("Returning existing schema: %r", location) self.schema._imports[namespace] = schema return schema # Silently ignore import statements which we can't resolve via the # namespace and doesn't have a schemaLocation attribute. if not location: logger.debug( "Ignoring import statement for namespace %r " + "(missing schemaLocation)", namespace) return # Load the XML schema_node = load_external(location, self.schema._transport, self.parser_context) # Check if the xsd:import namespace matches the targetNamespace. If # the xsd:import statement didn't specify a namespace then make sure # that the targetNamespace wasn't declared by another schema yet. schema_tns = schema_node.get('targetNamespace') if namespace and schema_tns and namespace != schema_tns: raise XMLParseError( ("The namespace defined on the xsd:import doesn't match the " "imported targetNamespace located at %r ") % (location)) elif schema_tns in self.parser_context.schema_objects: schema = self.parser_context.schema_objects.get(schema_tns) message = ("Skipping import of schema located at %r " + "for the namespace %r, since the namespace was " + "already imported with the schema located at %r") % ( location, namespace or '(null)', schema._location) warnings.warn(message, ZeepWarning, stacklevel=6) # If this schema location is 'internal' then retrieve the original # location since that is used as base url for sub include/imports if location in self.parser_context.schema_locations: base_url = self.parser_context.schema_locations[location] else: base_url = location schema = self.schema.__class__(schema_node, self.schema._transport, location, self.parser_context, base_url) self.schema._imports[namespace] = schema return schema
def visit_import(self, node, parent): """ <import id = ID namespace = anyURI schemaLocation = anyURI {any attributes with non-schema Namespace}...> Content: (annotation?) </import> """ schema_node = None namespace = node.get('namespace') location = node.get('schemaLocation') if location: location = absolute_location(location, self.document._base_url) if not namespace and not self.document._target_namespace: raise XMLParseError( "The attribute 'namespace' must be existent if the " "importing schema has no target namespace.") # Check if the schema is already imported before based on the # namespace. Schema's without namespace are registered as 'None' schema = self.schema._get_schema_document(namespace, fail_silently=True) if schema: if location and schema._location == location: logger.debug("Returning existing schema: %r", location) self.document.register_import(namespace, schema) return schema else: # Use same warning message as libxml2 message = ( "Skipping import of schema located at %r " + "for the namespace %r, since the namespace was " + "already imported with the schema located at %r" ) % (location, namespace or '(null)', schema._location) warnings.warn(message, ZeepWarning, stacklevel=6) return # Hardcode the mapping between the xml namespace and the xsd for now. # This seems to fix issues with exchange wsdl's, see #220 if not location and namespace == 'http://www.w3.org/XML/1998/namespace': location = 'https://www.w3.org/2001/xml.xsd' # Silently ignore import statements which we can't resolve via the # namespace and doesn't have a schemaLocation attribute. if not location: logger.debug( "Ignoring import statement for namespace %r " + "(missing schemaLocation)", namespace) return # Load the XML schema_node = load_external(location, self.schema._transport) # Check if the xsd:import namespace matches the targetNamespace. If # the xsd:import statement didn't specify a namespace then make sure # that the targetNamespace wasn't declared by another schema yet. schema_tns = schema_node.get('targetNamespace') if namespace and schema_tns and namespace != schema_tns: raise XMLParseError(( "The namespace defined on the xsd:import doesn't match the " "imported targetNamespace located at %r " ) % (location)) elif self.schema._has_schema_document(schema_tns): schema = self.schema._get_schema_document(schema_tns) message = ( "Skipping import of schema located at %r " + "for the namespace %r, since the namespace was " + "already imported with the schema located at %r" ) % (location, namespace or '(null)', schema._location) warnings.warn(message, ZeepWarning, stacklevel=6) schema = self.schema.create_new_document(schema_node, location) self.document.register_import(namespace, schema) return schema