I've been working on a lightweight XML schema parser, and have what I think is a moderately clean solution (some parts helped out by previous questions I posted here) so far for obtaining all schema details, but would like any criticism at all that could help further improve this code.
Below I have supplied the schema class I wrote, and then an example schema.txt file that the schema class will open if run as main. The schema class calls under "main" can be modified if you want to get a better look at the schema data structure, and I have some accompanying functions I have written for the class to pull out specific details that I haven't put here because I still need to do some work on them.
schema.py:
from lxml import etree
INDICATORS = ["all", "sequence", "choice"]
TYPES = ["simpleType", "complexType"]
class schema:
def __init__(self, schemafile):
if schemafile is None:
print "Error creating Schema: Invalid schema file used"
return
self.schema = self.create_schema(etree.parse(schemafile))
def create_schema(self, schema_data):
def getXSVal(element): #removes namespace
return element.tag.split('}')[-1]
def get_simple_type(element):
return {
"name": element.get("name"),
"restriction": element.getchildren()[0].attrib,
"elements": [ e.get("value") for e in element.getchildren()[0].getchildren() ]
}
def get_simple_content(element):
return {
"simpleContent": {
"extension": element.getchildren()[0].attrib,
"attributes": [ a.attrib for a in element.getchildren()[0].getchildren() ]
}
}
def get_elements(element):
if len(element.getchildren()) == 0:
return element.attrib
data = {}
ename = element.get("name")
tag = getXSVal(element)
if ename is None:
if tag == "simpleContent":
return get_simple_content(element)
elif tag in INDICATORS:
data["indicator"] = tag
elif tag in TYPES:
data["type"] = tag
else:
data["option"] = tag
else:
if tag == "simpleType":
return get_simple_type(element)
else:
data.update(element.attrib)
data["elements"] = []
data["attributes"] = []
children = element.getchildren()
for child in children:
if child.get("name") is not None:
data[getXSVal(child)+"s"].append(get_elements(child))
elif tag in INDICATORS and getXSVal(child) in INDICATORS:
data["elements"].append(get_elements(child))
else:
data.update(get_elements(child))
if len(data["elements"]) == 0:
del data["elements"]
if len(data["attributes"]) == 0:
del data["attributes"]
return data
schema = {}
root = schema_data.getroot()
children = root.getchildren()
for child in children:
c_type = getXSVal(child)
if child.get("name") is not None and not c_type in schema:
schema[c_type] = []
schema[c_type].append(get_elements(child))
return schema
def get_Types(self, t_name):
types = []
for t in self.schema[t_name]:
types.append(t["name"])
return types
def get_simpleTypes(self):
return self.get_Types("simpleType")
def get_complexTypes(self):
return self.get_Types("complexType")
if __name__ == '__main__':
fschema = open("schema.txt")
schema = schema(fschema)
print schema.get_simpleTypes()
print schema.get_complexTypes()
schema.txt:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="main_object">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element minOccurs="1" maxOccurs="1" name="source">
<xs:complexType>
<xs:all>
<xs:element name="name" type="xs:string" />
<xs:element name="group_id" type="xs:integer" />
<xs:element minOccurs="0" name="description" type="xs:string" />
</xs:all>
<xs:attribute name="id" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<xs:element minOccurs="1" maxOccurs="1" name="event">
<xs:complexType>
<xs:all>
<xs:element name="date" type="xs:date" />
<xs:element minOccurs="0" name="event_type" type="xs:string" />
<xs:element minOccurs="0" name="event_hours" type="xs:string" />
</xs:all>
<xs:attribute name="id" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="state">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
</xs:sequence>
<xs:attribute name="id" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="location">
<xs:complexType>
<xs:all>
<xs:element name="address" type="simpleAddressType" />
<xs:element minOccurs="0" name="directions" type="xs:string" />
<xs:element minOccurs="0" name="hours" type="xs:string" />
</xs:all>
<xs:attribute name="id" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="selection_item">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="selection_type_id" type="xs:integer" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="option_id">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:integer">
<xs:attribute name="sort_order" type="xs:integer" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="custom_selection">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="heading" type="xs:string" />
<xs:sequence maxOccurs="unbounded">
<xs:element name="response_id">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:integer">
<xs:attribute name="sort_order" type="xs:integer" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:choice>
<xs:attribute name="id" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="response">
<xs:complexType>
<xs:all>
<xs:element name="text" type="xs:string" />
<xs:element minOccurs="0" name="sort_order" type="xs:integer" />
</xs:all>
<xs:attribute name="id" type="xs:integer" use="required" />
</xs:complexType>
</xs:element>
</xs:choice>
<xs:attribute fixed="3.0" name="schemaVersion" type="xs:decimal" use="required" />
</xs:complexType>
</xs:element>
<xs:complexType name="simpleAddressType">
<xs:all>
<xs:element minOccurs="0" name="location_name" type="xs:string"/>
<xs:element name="line1" type="xs:string"/>
<xs:element minOccurs="0" name="line2" type="xs:string"/>
<xs:element minOccurs="0" name="line3" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="state" type="xs:string"/>
<xs:element name="zip" type="xs:string"/>
</xs:all>
</xs:complexType>
<xs:complexType name="certified">
<xs:simpleContent>
<xs:extension base="xs:integer">
<xs:attribute name="certification" type="certificationEnum" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="certificationEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="unofficial_partial"/>
<xs:enumeration value="unofficial_complete"/>
<xs:enumeration value="certified"/>
<xs:enumeration value="Unofficial_partial"/>
<xs:enumeration value="Unofficial_complete"/>
<xs:enumeration value="Unofficial_Partial"/>
<xs:enumeration value="Unofficial_Complete"/>
<xs:enumeration value="Certified"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="yesNoEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="yes"/>
<xs:enumeration value="no"/>
<xs:enumeration value="Yes"/>
<xs:enumeration value="No"/>
<xs:enumeration value="YES"/>
<xs:enumeration value="NO"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>