def parse_xsd(self, filepath=None, from_string=None, encoding=None): if filepath: parser = etree.XMLParser(encoding=encoding) self.xs_etree = etree.parse(filepath, parser=parser) elif from_string: self.xs_etree = etree.fromstring(from_string) else: raise XSInputError() self.root = self.xs_etree.getroot() ns_xs = self.root.nsmap.get(self.schema_ns) if ns_xs: self._ns_path = ns_xs sorted_nodes = self.determine_sequence() for primary_node in sorted_nodes: if primary_node.tag == self.get_tag('simpleType'): s_type = self.make_simple_type(primary_node) if s_type.name is None: raise XSParserError('XSD-schema error: external xs:simpleType not found name') self.main_map.set(s_type.name, s_type) if primary_node.tag == self.get_tag('complexType'): c_type = self.make_complex_type(primary_node, as_external=True) if c_type.name is None: raise XSParserError('XSD-schema error: external xs:complexType not found name') self.main_map.set(c_type.name, c_type) if primary_node.tag == self.get_tag('element'): element = self.make_element(primary_node, as_external=True) if element.name is None: raise XSParserError('XSD-schema error: external xs:element not found name') self.main_map.set(element.name, element) if element.complex_type: self.external_objects.append(element)
def make_complex_type(self, node, as_external=False): _xs = self.schema_ns name = node.attrib.get('name') documentation = '' doc = self.xpath_get(node, '{0}:annotation/{0}:documentation'.format(_xs), namespaces=node.nsmap) if doc is not None: documentation = doc.text c_type = XSComplexType(name=name) c_type.documentation = documentation for node_attribute in self.xpath_list(node, '{0}:attribute'.format(_xs), namespaces=node.nsmap): attr_name = node_attribute.attrib.get('name') attribute = XSAttribute( name=attr_name, required=node_attribute.attrib.get('use') == 'required' ) s_type_name = node_attribute.attrib.get('type') s_type_node = None if not s_type_name: s_type_node = self.xpath_get(node_attribute, '{}:simpleType'.format(_xs), namespaces=node_attribute.nsmap) if s_type_name: s_type_name = node_attribute.attrib.get('type') s_type = self.main_map.get(s_type_name) if s_type is not None: attribute.simple_type = s_type else: raise XSParserError('Attribute {} not found simpleType {}'.format(attr_name, s_type_name)) elif s_type_node is not None: s_type = self.make_simple_type(s_type_node) if s_type is not None: attribute.simple_type = s_type else: raise XSParserError('Attribute {} contains broken simpleType'.format(attr_name)) else: raise XSParserError('XSD-schema error: xs_attribute {} not found simpleType'.format(attr_name)) c_type.add_attribute(attribute) node_sequence = self.xpath_get(node, '{}:sequence'.format(_xs), namespaces=node.nsmap) if node_sequence is not None: self._make_sequence(node_sequence, c_type, as_external=as_external) node_choice = self.xpath_get(node, '{}:choice'.format(_xs), namespaces=node.nsmap) if node_choice is not None: self._make_choice(node_choice, c_type, as_external=as_external) return c_type
def get_etree_relations(self, node, containers=None): assert containers container = [] containers.append(container) needed_tags = [self.get_tag('sequence'), self.get_tag('choice')] for sub_node in node.getchildren(): if sub_node.tag in needed_tags: for rel_element in sub_node.getchildren(): if rel_element.attrib.get('type'): for c in containers: c.append(rel_element.attrib.get('type')) elif rel_element.attrib.get('name'): if rel_element.xpath('complexType'): for c in containers: c.append(rel_element.attrib.get('name')) self.add_ct_relation(sub_node.attrib.get('name'), self.get_etree_relations( rel_element, containers=containers )) elif isinstance(rel_element, etree._Comment): continue else: raise XSParserError('Relation type name not found') return container
def ct_sorted(ct_relation_dict): """ Функция принимает словарь {"название": СTRelation}. Возвращает отсортированный список CTRelation. Гарантируется что элемент будет находится после связаных с ним элементов. """ result_names = [] ct_relation_list = ct_relation_dict.values() current_step = ct_relation_list _round = 0 while True: for_next_step = [] for ct_relation in current_step: if not len(ct_relation.relation_list): result_names.insert(0, ct_relation.name) else: indexes = [] for rel in ct_relation.relation_list: try: indexes.append(result_names.index(rel)) except ValueError: for_next_step.append(ct_relation) break if not len(indexes): continue result_names.insert(max(indexes)+1, ct_relation.name) if _round and not _round % 10: print('Internal warning: Sorting of complexTypes passed through {} rounds'.format(_round)) if _round > 1000: raise XSParserError('complexType relation deadlock found.') if len(for_next_step): current_step = for_next_step _round += 1 else: break return map(lambda x: ct_relation_dict.get(x), result_names)
def make_element(self, node, as_external=False, under_choice=False): _xs = self.schema_ns name = node.attrib.get('name') documentation = '' doc = self.xpath_get(node, '{0}:annotation/{0}:documentation'.format(_xs), namespaces=node.nsmap) if doc is not None: documentation = doc.text el = XSElement(name=name) el.documentation = documentation el.under_choice = under_choice min_occurs = node.attrib.get('minOccurs') if min_occurs: el.min_occurs = int(min_occurs) max_occurs = node.attrib.get('maxOccurs') if max_occurs: el.max_occurs = 0 if max_occurs == 'unbounded' else int(max_occurs) ttype = node.attrib.get('type') if ttype: parent_type = self.main_map.get(ttype) if isinstance(parent_type, XSComplexType): el.complex_type = parent_type elif isinstance(parent_type, XSSimpleType): el.simple_type = parent_type else: raise XSParserError('type {} not found for element {}'.format(ttype, name)) #todo element "base" attribute not implemented else: child_ctype_node = self.xpath_get(node, '{}:complexType'.format(_xs), namespaces=node.nsmap) if child_ctype_node is not None: el.complex_type = self.make_complex_type(child_ctype_node, as_external=as_external) else: child_stype_node = self.xpath_get(node, '{}:simpleType'.format(_xs), namespaces=node.nsmap) if child_stype_node is not None: el.simple_type = self.make_simple_type(child_stype_node) if not (el.complex_type or el.simple_type): raise XSParserError('xs_type not found for element {}'.format(name)) return el
def make_simple_type(self, node): _xs = self.schema_ns name = node.attrib.get('name') documentation = '' doc = self.xpath_get(node, '{0}:annotation/{0}:documentation'.format(_xs), namespaces=node.nsmap) if doc is not None: documentation = doc.text restriction = self.xpath_get(node, './/{0}:restriction[@base]'.format(_xs), namespaces=node.nsmap) base = restriction.attrib.get('base') if not base: raise XSParserError('Internal error: restriction base not found on simpleType') if base == _xs+':string': s_type = XSStringType(name=name, documentation=documentation) elif base == _xs+':integer': s_type = XSIntegerType(name=name, documentation=documentation) elif base == _xs+':decimal': s_type = XSDecimalType(name=name, documentation=documentation) elif base == _xs+':date': s_type = XSDateType(name=name, documentation=documentation) elif self.main_map.get(base): s_type = self.main_map.get(base) else: raise XSParserNotImplemented('simpleType with base="{} now is not implemented"'.format(base)) for key, field_name in s_type.available_restriction_map.items(): if field_name in s_type.multiple_fields: container = getattr(s_type, field_name, []) for param in self.xpath_list(restriction, _xs + ':{}'.format(key), namespaces=restriction.nsmap): container.append(param.attrib.get('value')) else: _ = self.xpath_get(restriction, _xs + ':{}'.format(key), namespaces=node.nsmap) if _ is not None: setattr(s_type, field_name, _.attrib.get('value')) return s_type
def determine_sequence(self): # Возвращает элементы первого уровня схемы в необходимом порядке их загрузки. # зависимые ноды идут последними # Список simpleType. обрабатываются самые первые simple_types = [] # Список element. Должны обработаться после complexType и simpleType raw_elements = [] # Список complexType. Последовательность определить по ct_relations complex_types_map = {} for schema_node in self.root.getchildren(): if isinstance(schema_node, etree._Comment): continue if schema_node.tag == self.get_tag('element'): raw_elements.append(schema_node) elif schema_node.tag == self.get_tag('complexType'): node_name = schema_node.attrib.get('name') complex_types_map[node_name] = schema_node relation_containers = [] first_container = [] relation_containers.append(first_container) self.add_ct_relation(node_name, self.get_etree_relations(schema_node, containers=relation_containers)) elif schema_node.tag == self.get_tag('simpleType'): simple_types.append(schema_node) sorted_complex_types = [] if self.ct_relations: self.remove_stypes_from_ct_relations(simple_types) for ct in ct_sorted(self.ct_relations): sorted_ct_node = complex_types_map.get(ct.name) if sorted_ct_node is None: XSParserError('Internal error: ComplexType node not found by name') sorted_complex_types.append(sorted_ct_node) result = simple_types + sorted_complex_types + raw_elements return result