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/>节点看起来像这样:

<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()函数上,以读取“密钥”并决定如何处理该值。 分析应用程序表明,大约 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 模式规范,我可以<xs:sequence>在 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 文档中强制执行属性顺序?如果答案是“否”——有人可能会提出一种不会带来巨大运行时性能损失的替代方案吗?

4

8 回答 8

37

根据xml规范,

开始标签或空元素标签中属性规范的顺序不重要

您可以在第 3.1 节查看

于 2009-11-05T17:28:37.420 回答
6

XML 属性没有顺序,因此没有强制执行的顺序

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

于 2009-11-05T19:12:14.963 回答
4

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

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

于 2009-11-06T01:10:50.497 回答
2

答案否定的,唉。我被你 40% 的数字吓到了。我很难相信将“foo”变成 ProcessFoo 需要这么长时间。您确定 40% 不包括执行ProcessFoo 所需的时间吗?

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

于 2009-11-05T17:26:04.460 回答
1

我认为 XML Schema 不支持这一点——属性只是由名称定义和限制,例如它们必须匹配特定的名称——但我看不出如何在 XSD 中定义这些属性的顺序。

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

于 2009-11-05T17:22:35.187 回答
1

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

如果您只是假设第一个属性是“id”,并测试了名称以确保确定呢?如果是,则使用该值,如果不是,则可以尝试通过名称获取属性或丢弃文档。

虽然不如按其序号调用属性那么有效,但您可以猜到您的数据提供者已将 XML 交付给规范的一些非零次数。其余时间,您可以采取其他措施。

于 2009-11-05T17:25:00.120 回答
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 不会强制执行任何命令。但这并不意味着解析器不能被愚弄快速工作,所以我保留上述答案以防万一它真的有效。

于 2009-11-05T17:23:28.803 回答
0

据我回忆,Expat 是一个非验证解析器,并且对它更好。所以你可能会放弃那个 XSD 想法。在许多 XML 方法中,依赖顺序也不是一个好主意(XSD 在元素顺序上受到了很多批评,例如,MSFT 的 XML Web 服务的支持者或反对者)。

进行自定义编码并简单地扩展您的逻辑以更有效地查找或挖掘解析器源。编写围绕编码有效替换的工具,同时保护软件代理和用户免受它的影响是微不足道的。你想要这样做,以便它易于迁移,同时保持向后兼容性和可逆性。此外,请使用固定大小的约束/属性名称翻译。

[ 认为自己很幸运拥有 Expat :) 及其原始速度。想象一下 CLR 开发人员是如何喜欢 XML 缩放工具的,他们通常会在“仅查询数据库”的过程中通过网络发送 200MB 的数据......]

于 2009-11-05T17:38:48.053 回答