Can I enforce the order of XML attributes using a schema?

expat-parser, performance, xml, xsd

Solution

According to the xml specification,

the order of attribute specifications in a start-tag or empty-element tag is not significant

You can check it at section 3.1

Problem

Our C++ application reads configuration data from XML files that look something like this: ``` <data> <value id="FOO1" name="foo1" size="10" description="the foo" ... /> <value id="FOO2" name="foo2" size="10" description="the other foo" ... /> ... <value id="FOO300" name="foo300" size="10" description="the last foo" ... /> </data> ``` The complete application configuration consist of ~2500 of these XML files (which translates into more than 1.5 million key/value attribute pairs). The XML files come from many different sources/teams and are validated against a schema. However, sometimes the `<value/>` nodes look like this: ``` <value name="bar1" id="BAR1" description="the bar" size="20" ... /> ``` or this: ``` <value id="BAT1" description="the bat" name="bat1" size="25" ... /> ``` To make this process fast, we are using Expat to parse the XML documents. Expat exposes the attributes as an array - like this: ``` void ExpatParser::StartElement(const XML_Char* name, const XML_Char** atts) { // The attributes are stored in an array of XML_Char* where: // the nth element is the 'key' // the n+1 element is the value // the final element is NULL for (int i = 0; atts[i]; i += 2) { std::string key = atts[i]; std::string value = atts[i + 1]; ProcessAttribute (key, value); } } ``` This puts all the responsibility onto our `ProcessAttribute()` function to read the 'key' and decide what to do with the value. Profiling the app has shown that ~40% of the total XML Parsing time is dealing with these attributes by name/string. The overall process could be sped up dramatically if I could guarantee/enforce the order of the attributes (for starters, no string comparisons in `ProcessAttribute()`). For example, if 'id' attribute was always the 1st attribute we could deal with it directly: ``` void ExpatParser::StartElement(const XML_Char* name, const XML_Char** atts) { // The attributes are stored in an array of XML_Char* where: // the nth element is the 'key' // the n+1 element is the value // the final element is NULL ProcessID (atts[1]); ProcessName (atts[3]); //etc. } ``` According to the W3C schema specs, I can use `<xs:sequence>` in an XML schema to enforce the order of elements - but it doesn't seem to work for attributes - or perhaps I'm using it incorrectly: ``` <xs:element name="data"> <xs:complexType> <xs:sequence> <xs:element name="value" type="value_type" minOccurs="1" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="value_type"> <!-- This doesn't work --> <xs:sequence> <xs:attribute name="id" type="xs:string" /> <xs:attribute name="name" type="xs:string" /> <xs:attribute name="description" type="xs:string" /> </xs:sequence> </xs:complexType> ``` Is there a way to enforce attribute order in an XML document? If the answer is "no" - could anyone perhaps suggest a alternative that wouldn't carry a huge runtime performance penalty?

Original source