我能否使用模式来强制XML属性的顺序?

19
我们的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 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”,并决定如何处理该值。应用程序的分析显示,约40%的XML解析时间用于按名称/字符串处理这些属性。
如果我能保证/强制属性的顺序(首先,在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: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文档中属性的顺序?如果答案是“否”,是否有人可以建议一种替代方案,而不会带来巨大的运行时性能损失?


2
为什么你选择了使用属性而不是 <value><id>FOO1</id><name>foo1</name><size>10</size><description>This is the description</description></value>?你可以指定元素的顺序,那为什么不使用它们呢? - jmucchiello
1
+1 这是一个非常好的(并且有趣的)问题。 - Mark E. Haase
8个回答

38
根据xml规范,起始标签或空元素标签中属性规范的顺序并不重要。你可以在3.1章节查看。

6

XML属性没有顺序,因此无法强制排序。

如果您需要有序的内容,您需要使用XML元素。或者使用不同于XML的其他格式,例如JSON、YAML和bEncode,这些格式既有映射(无序的)又有序列(有序的)。


5

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

如果我有任何涉及2,500个XML文件和1.5百万个键/值对的过程,我会尽快将数据从XML中提取出来并转换为更可用的格式。可以使用数据库、二进制序列化格式等。除了模式验证之外,您没有从使用XML中获得任何优势。我会在每次获取新的XML文件时更新我的存储,并将解析1.5百万个XML元素从主流程中剥离出来。


2

很抱歉,答案是不行的。我对你说的40%数字感到惊讶,转换“foo”为“ProcessFoo”需要那么长时间让我难以置信。你确定这40%的时间不包括执行“ProcessFoo”的时间吗?

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


2
"Expat thing"是最快的解析器之一。别惊讶,你刚被微软和IBM出售了无法扩展的XML :-) - rama-jka toti
1
Gary,你说得对。我没有详细说明ProccessAttribute()函数的确切作用,因为我认为这与原始问题无关...我们在应用程序启动时解析这些XML文档,并将元素数据转储到SQLite数据库中以供后续处理。SQLite API允许按索引绑定参数-因此,如果我能确信XML属性始终按照Insert语句中的参数顺序排列,事情会变得更快(快得多)。 - Mike Willekes

1

我认为XML Schema不支持这个功能 - 属性只能通过名称进行定义和限制,例如它们必须匹配特定的名称 - 但我不知道如何在XSD中定义这些属性的顺序。

我不知道有什么其他方法可以确保XML节点上的属性按特定顺序出现 - 不确定其他XML模式机制(如Schematron或Relax NG)是否支持此功能....


4
这不是XML模式的限制,而是XML本身的限制。请参见st.stoqnov的评论。 - porges

1

我相信在XML文档中没有强制属性顺序的方法。我假设您可以通过业务流程或其他人为因素(例如合同或其他文件)坚持要求它。

如果您假设第一个属性是“id”,并测试名称是否正确,会怎样呢?如果是,则使用该值;如果不是,则可以尝试按名称获取属性或放弃该文档。

虽然不如按其序号调用属性高效,但您仍然有一定的可能性猜测到数据提供者已按规范提供了XML。其余时间,您可以采取其他措施。


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不会强制执行任何顺序。但这并不意味着解析器不能被快速处理,所以我将上面的答案保留下来,以防它真的有效。


0
据我回忆,Expat 是一个非验证解析器,更适合于此..因此您可以放弃 XSD 的想法。在许多 XML 方法中,顺序相关并不是一个好主意(例如,XSD 在元素顺序上曾经受到大量批评,由 MSFT 的 XML Web Services 的支持者或反对者提出)。
进行自定义编码,然后简单地扩展逻辑以进行更有效的查找,或深入解析器源代码。编写围绕编码有效替换的工具非常容易,同时保护软件代理和用户免受其影响。您希望轻松迁移它,同时保留向后兼容性和可逆性。此外,选择固定大小约束/属性名称转换。
[幸运的是,您有 Expat :) 和它的原始速度。想象一下 CLR 开发人员喜欢 XML 扩展设施,他们通常在“只查询数据库”的过程中发送200MB..]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接