我可以使用模式强制执行XML属性的顺序吗?

时间:2009-11-05 17:13:17

标签: xml performance xsd expat-parser

我们的C ++应用程序从XML文件中读取配置数据,如下所示:

<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>

完整的应用程序配置包含大约2500个这些XML文件(转换为超过150万个键/值属性对)。 XML文件来自许多不同的源/团队,并根据模式进行验证。但是,有时<value/>节点看起来像这样:

<value name="bar1" id="BAR1" description="the bar" size="20" ... />

或者这个:

<value id="BAT1" description="the bat" name="bat1"  size="25" ... />

为了快速完成此过程,我们使用Expat来解析XML文档。 Expat将属性公开为数组 - 如下所示:

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);
 }
}

这将所有责任放在我们的ProcessAttribute()函数上以读取'key'并决定如何处理该值。 对应用进行概要分析表明,XML解析总时间的约40%是按名称/字符串处理这些属性。

如果我可以保证/强制执行属性的顺序(对于初学者,ProcessAttribute()中没有字符串比较),整个过程可能会大大加快。例如,如果'id'属性始终第一个属性,我们可以直接处理它:

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.
}

根据W3C架构规范,我可以在XML架构中使用<xs:sequence>来强制执行元素的顺序 - 但它似乎不适用于属性 - 或者我可能错误地使用它:< / p>

<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>

有没有办法在XML文档中强制执行属性顺序?如果答案是“否” - 那么是否有人可能建议一种不会带来巨大运行时性能损失的替代方案?

8 个答案:

答案 0 :(得分:34)

根据xml规范,

  

start-tag或empty-element标记中属性规范的顺序不重要

您可以在section 3.1

查看

答案 1 :(得分:6)

XML属性没有订单,因此没有强制执行的命令。

如果您想要订购的东西,则需要XML元素。或者与XML不同的东西。 JSON,YAML和bEncode,例如有两个映射(无序)和序列(有序)。

答案 2 :(得分:4)

正如其他人所指出的那样,不,你不能依赖属性排序。

如果我有任何涉及2,500个XML文件和150万个键/值对的进程,我会尽快从XML中获取数据并使用更可用的形式。数据库,二进制序列化格式,等等。您没有从使用XML(除了模式验证)中获得任何优势。每次我获得一个新的XML文件时,我都会更新我的商店,并从我的流程主流中解析出150万个XML元素。

答案 3 :(得分:2)

答案不,唉。你对40%的数字感到震惊。我发现很难相信将“foo”变成ProcessFoo需要那么长时间。你确定40%不包括执行 ProcessFoo所需的时间吗?

是否可以使用此Expat来按名称访问属性?这是访问属性的更传统方式。我不是说它会更快,但它可能值得一试。

答案 4 :(得分:1)

我认为XML Schema不支持 - 属性只是按名称定义和限制,例如他们必须匹配一个特定的名称 - 但我不知道如何在XSD中定义这些属性的订单。

我不知道有任何其他方法可以确保XML节点上的属性按特定顺序排列 - 不确定是否有任何其他XML模式机制(如Schematron或Relax NG)都支持....

答案 5 :(得分:1)

我很确定无法在XML文档中强制执行属性顺序。我将假设您可以通过业务流程或其他人为因素(例如合同或其他文档)坚持这一点。

如果您只是假设第一个属性是“id”,并测试了名称以确定该怎么办?如果是,请使用该值,否则,您可以尝试按名称获取属性或丢弃文档。

虽然不如通过序数调用属性那么高效,但是你可以猜测数据提供者已经将XML提供给规范的次数非零次。剩下的时间,你可以采取其他行动。

答案 6 :(得分:0)

只是一个猜测,但您可以尝试在每个属性规范中添加use="required"吗?

<xs:complexType name="value_type">
 <!-- This doesn't work -->
 <xs:sequence>
  <xs:attribute name="id" type="xs:string" use="required" />
  <xs:attribute name="name" type="xs:string" use="required" />
  <xs:attribute name="description" type="xs:string" use="required" />
 </xs:sequence>
</xs:complexType>

我想知道解析器是否因为允许可选属性而变慢,当看起来你的属性总是存在时。

再次,只是一个猜测。

编辑: XML 1.0规范说属性顺序不重要。 http://www.w3.org/TR/REC-xml/#sec-starttags

因此,XSD不会强制执行任何订单。但这并不意味着解析器不能被快速愚弄,所以我保留上述答案,以防它真正起作用。

答案 7 :(得分:0)

从我记得的情况来看,Expat是一个非验证解析器,对它来说更好..所以你可能会废弃那个XSD的想法。在许多XML方法中,顺序依赖也不是一个好主意(XSD在当时的元素顺序上受到了批评,例如,在MSFT的XML Web Services的专业或反卖家)。

执行自定义编码,只需扩展逻辑,以便更有效地查找或深入了解解析器源代码。编写围绕编码有效替换的工具同时保护软件代理和用户免受它的影响是微不足道的..你想做到这一点,以便在保持向后兼容性和可逆性的同时轻松迁移。另外,请使用固定大小的约束/属性名称转换。

[考虑自己幸运的是Expat :)及其原始速度。想象一下CLR开发人员如何喜欢XML扩展设施,他们通常在“只是查询数据库”的过程中发送200MB的数据线。]