设计XML模式的最佳实践是什么?

44
作为一名业余软件开发人员(我仍在学术界),我曾经为XML文档编写过几个模式。由于我不完全确定XML的语义是什么,所以我经常遇到设计失误,导致生成的XML文档看起来很丑陋。

我的假设:

<property> value </property>

属性 = 值

<property attribute="attval"> value </property>

一种带有特殊描述符的属性,即“属性”(attribute)。

<parent>
  <child> value </child>
</parent>
父元素具有一个名为“child”的特征,其值为“value”。
<tag />

"标签"是一个标志,或者直接翻译为文本。我对这个不太确定。

<parent>
  <child />
</parent>
"

"child"表示"parent"。"child"是一个标志或布尔值。我对这个不太确定。

如果你想表示笛卡尔坐标系,就会出现歧义:

"
<coordinate x="0" y="1" />

<coordinate> 0,1 </coordinate>

<coordinate> <x> 0 </x> <y> 1 </y> </coordinate>

这些选项中哪一个最正确?基于我对XML模式设计的当前理解,我会倾向于第三个选项,但我确实不确定。

有哪些简洁地描述如何有效设计XML模式的资源?


1
好问题,可惜没有确定的答案 :) 但至少我知道没人在意,所以我可以随意设计我的模式 :) - dr. evil
14个回答

25

一个普遍(但重要!)的建议是永远不要在单个节点(无论是文本节点还是属性节点)中存储多个逻辑数据。否则,您最终需要在框架提供给您的XML解析逻辑之上编写自己的解析逻辑。

因此,在您的坐标示例中,<coordinate x="0" y="1" /><coordinate> <x>0</x> <y>1</y> </coordinate>对我来说都是合理的。

但是<coordinate> 0,1 </coordinate>不太好,因为它将两个逻辑数据(X坐标和Y坐标)存储在单个XML节点中,强制使用者在其XML解析器之外解析数据。虽然通过逗号切分字符串非常简单,但仍存在一些歧义,例如如果末尾有额外的逗号会发生什么。


17

2
+1 给 Roger 的网站点赞。xFront 上有很多关于 XML 和 XML Schema 的好东西。 - james.garriss
1
@james.garriss,是的。Roger不断地推动教授XML Schema、XPath和XSLT等最先进主题的边界。 - Dimitre Novatchev

11

我同意以下cdragon的建议,避免选项#2。选择#1和#3主要是风格问题。我喜欢使用属性来表示实体的属性,使用元素来表示数据。有时候很难分类。但两者都不是“错误”的。

顺便说一下模式设计的话题,我想提出我偏爱的最大复用级别(元素和类型),这也可以促进在数据库中存储的数据字典中对这些实体的外部“逻辑”引用。

请注意,“伊甸园”模式提供了最大的复用性,但也需要最多的工作量。在本文底部,我提供了博客系列中涵盖的其他模式的链接。

伊甸园方法 http://blogs.msdn.com/skaufman/archive/2005/05/10/416269.aspx

使用模块化方法定义所有元素,并像百叶窗方法一样声明所有类型定义为全局。每个元素被全局定义为节点的直接子元素,并且其类型属性可以设置为命名复杂类型之一。

<?xml version="1.0" encoding="UTF-8"?> 
<xs:schema targetNamespace="TargetNamespace" xmlns:TN="TargetNamespace" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  elementFormDefault="qualified" attributeFormDefault="unqualified"/> 
<xs:element name="BookInformation" type="BookInformationType"/> 
  <xs:complexType name="BookInformationType"/> 
    <xs:sequence> 
      <xs:element ref="Title"/> 
      <xs:element ref="ISBN"/> 
      <xs:element ref="Publisher"/> 
      <xs:element ref="PeopleInvolved" maxOccurs="unbounded"/> 
    </xs:sequence> 
  </xs:complexType> 
  <xs:complexType name="PeopleInvolvedType"> 
    <xs:sequence> 
      <xs:element name="Author"/> 
    </xs:sequence> 
  </xs:complexType> 
  <xs:element name="Title"/> 
  <xs:element name="ISBN"/> 
  <xs:element name="Publisher"/> 
  <xs:element name="PeopleInvolved" type="PeopleInvolvedType"/> 
</xs:schema>

这种方法的优点是模式可重用。由于元素和类型都是全局定义的,因此两者都可以重复使用。这种方法提供了最大量的可重复使用内容。
缺点是模式过于冗长。
当您正在创建通用库时,可以对模式元素和类型的范围及其在其他模式中的使用特别是在可扩展性和模块化方面不作任何假设时,这将是一个适当的设计。


由于每个不同的类型和元素都具有单个全局定义,这些基本粒子/组件可以一对一地与数据库中的标识符相关联。尽管乍一看似乎需要费力的手动维护文本XSD粒子/组件与数据库之间的关联,但实际上SQL Server 2005可以通过语句生成规范模式组件标识符。

CREATE XML SCHEMA COLLECTION

http://technet.microsoft.com/en-us/library/ms179457.aspx

相反,为了从规范粒子构建模式,SQL Server 2005提供了{{}}。
SELECT xml_schema_namespace function

http://technet.microsoft.com/en-us/library/ms191170.aspx

ca·non·i·cal 与数学相关。(方程、坐标等) "以最简或标准形式" http://dictionary.reference.com/browse/canonical

其他更易构建但不太可重用/更"非规范化/冗余"的模式包括

俄罗斯套娃方法 http://blogs.msdn.com/skaufman/archive/2005/04/21/410486.aspx

模式只有一个全局元素-根元素。所有其他元素和类型逐渐嵌套,因为每个类型都适合于其上面的类型而得名。由于该设计中的元素是本地声明的,因此它们将无法通过导入或包含语句进行重用。

薄片萨拉米方法 http://blogs.msdn.com/skaufman/archive/2005/04/25/411809.aspx

所有元素都在全局范围内定义,但类型定义在本地定义。这样其他模式可以重用元素。使用此方法,带有其本地定义类型的全局元素提供了元素内容的完整描述。这些信息“片段”被单独声明,然后聚合在一起,并且还可以组合在一起构建其他模式。

百叶窗式方法 http://blogs.msdn.com/skaufman/archive/2005/04/29/413491.aspx

与俄罗斯套娃方法类似,它们都使用单个全局元素。百叶窗式方法通过为所有类型定义命名和定义全局来描述一种模块化方法(与切片法不同,后者在全局声明元素并在本地声明类型)。每个全局定义的类型描述了一个单独的“板条”,可以被其他组件重用。此外,所有本地声明的元素可以是命名空间限定或非命名空间限定(根据模式顶部的elementFormDefault属性设置,“板条”可以“打开”或“关闭”)。


3

XML的设计有些主观性-我认为没有确切的指导方针来规定元素和属性应该如何布局,但我倾向于使用元素来表示“事物”,使用属性来表示它们的单个属性/属性。

就坐标示例而言,两者都是完全可以接受的,但我的倾向是使用<coordinate x="" y=""/>,因为它更加简洁,并且如果您有很多这样的元素,则使文档更易读。

最重要的是模式的命名空间。确保(a)您有一个,(b)您在其中有一个版本,以便将来可以更改内容并发布新版本。版本可以是日期或数字,例如:

http://company.com/2008/12/something/somethingelse/
urn:company-com:2008-12:something:somethingelse

http://company.com/v1/something/somethingelse/
urn:company-com:v1:something:somethingelse

3

我不知道任何关于如何设计XML文档模型(模式只是指定文档模型的正式方式)的好学习资源。

在我看来,理解XML的一个关键洞见是它不是一种语言:它是一种语法。每个文档模型都是一种独立的语言。

不同的文化将以自己特殊的方式使用XML。即使在W3C规范中,您也可以在XSLT的破折号分隔名称中闻到Lisp,在XML Schema的camelCaseNames中闻到Java。同样,不同的应用程序域将要求不同的XML语言习惯。

叙述性文档模型(如HTMLDocBook)倾向于将可打印文本放在文本节点中,并将元数据放在元素名称和属性中。

更多面向对象的文档模型(如SVG)几乎不使用文本节点,而是仅使用元素和属性。

我的个人文档模型设计经验法则大致如下:

如果它是需要mixed content的自由标记,使用HTML和DocBook作为灵感来源。其他规则只在其他情况下相关。 如果一个值将是复合或分层的,请使用元素。XML数据不应需要进一步解析,除了已经建立的惯例,如简单的空格分隔序列IDREFS之类的惯例。 如果一个值可能需要出现多次,请使用元素。 如果一个值可能需要进一步细化或稍后丰富,请使用元素。 如果一个值明显是原子的(布尔值、数字、日期、标识符、简单标签)并且最多只能出现一次,则使用属性。
另一种说法可能是: 如果是叙述性的,那就不是面向对象的。 如果是面向对象的,将对象建模为元素,原子属性为属性。
编辑:有些人似乎喜欢完全放弃属性。这没有什么问题,但我不喜欢它,因为它会使文档膨胀,并使它们难以手工阅读和编写。

1

在设计基于XML的格式时,通常需要考虑你所代表的内容。尝试模拟一些符合你意图的XML数据。一旦你得到了满足要求的数据,就可以开发模式来验证它。

在设计格式时,我倾向于使用元素来保存数据内容,使用属性来应用数据的特征,例如id、名称、类型或其他有关元素包含的数据的元数据。

在这方面,坐标的XML表示可能是:

<coordinate type="cartesian">
  <ordinate name="x">0</ordinate>
  <ordinate name="y">1</ordinate>
</coordinate>

这适用于不同的坐标系。如果您知道它们始终是笛卡尔坐标系,则更好的实现可能是:

<coordinate>
  <x>0</x>
  <y>1</y>
</coordinate>

当然,后者可能会导致更冗长的模式,因为每个元素类型都需要声明(尽管我希望定义一个复杂类型来实际完成这些元素的工作)。

就像在编程中一样,通常有多种实现相同目标的方法,但在许多情况下并没有对错之分,只有更好和更差之分。重要的是保持一致,并尝试使其直观,以便其他人查看您的模式时可以理解您的意图。

您应该始终对模式进行版本控制,并确保针对您的模式编写的XML表明了它的版本。如果您没有正确地对XML进行版本控制,则在支持针对旧模式编写的XML的同时进行模式的补充将变得更加困难。


1
我被指定编写一些XML模式,以将我们公司的系统与客户集成。我设计了十多个模式,超过10年前,发现规范中的许多扩展功能在实践中效果不佳。在设计新模式之前,我搜索了当前的最佳实践(并来到了这里!)。
上面的一些提示很有用,但我几乎不喜欢所有的参考文献。我找到的最好的设计建议来自Microsoft。
最好的参考是XML Schema Design Patterns: Avoiding Complexity。在这里,您将找到以下明智的建议:
“看起来许多模式作者最好理解和利用W3C XML模式提供的有效特性子集,而不是试图理解语言的所有玄学和细节。”
并详细解释以下准则:
  • 为什么应该使用全局和局部元素声明
  • 为什么应该使用全局和局部属性声明
  • 为什么应该了解XML命名空间如何影响W3C XML Schema
  • 为什么应该始终将elementFormDefault设置为“qualified”
  • 为什么应该使用属性组
  • 为什么应该使用模型组
  • 为什么应该使用内置的简单类型
  • 为什么应该使用复杂类型
  • 为什么不应该使用符号声明
  • 为什么应该谨慎使用替换组
  • 为什么应该优先使用键/键引用/唯一性约束而非ID/IDREF
  • 为什么应该谨慎使用变色龙模式
  • 为什么不应该使用默认值或固定值,特别是对于xs:QName类型
  • 为什么应该使用简单类型的限制和扩展
  • 为什么应该使用复杂类型的扩展
  • 为什么应该谨慎使用复杂类型的限制
  • 为什么应该谨慎使用抽象类型
  • 使用通配符提供明确定义的可扩展点
  • 不要使用组或类型重定义
我的建议是,当他们说“小心使用”时,你应该避免使用。我觉得Schema规范不是由软件开发人员编写的。他们试图使用一些面向对象的概念,但却搞砸了。很多扩展机制是无用的或者极其冗长的。我真的不明白为什么有人会发明复杂类型的限制。
这个网站上还有两篇不错的文章:
- 模式设计:如何处理变化 - XML Schema设计模式:复杂类型派生是否必要? 另外一个普遍的提示是,用不同于官方规范的方式指定你的模式。Relax NG似乎是最受欢迎的规范语言。不幸的是,你将失去其中一个最好的特性,即标准化。

1
在我们的Java项目中,我们经常使用JAXB来自动解析XML并将其转换为对象结构。我猜其他语言也会有类似的工具。一个合适的生成器可以自动创建您选择的编程语言中的对象结构。这使得处理XML通常更容易,同时仍然具有可移植的XML表示形式,用于系统之间的通信。
如果您确实使用了这样的自动映射,您会发现这会大大限制模式 - <coordinate> <x> 0 </x> <y> 1 </y> </coordinate> 是正确的方式,除非您想在翻译中进行特殊操作。您最终将得到一个名为Coordinate的类,其中包含两个属性xy,其类型与模式中声明的相应类型相同。

0

我经常发现自己在同一个问题上挣扎,但实际上并不重要,XML只是数据。

话虽如此,我通常更喜欢“如果它说节点的某些内容,那么它就是属性,否则它就是子节点”的方法。

在你的例子中,我会选择:

<coordinate>
    <x>0</x>
    <y>1</y>
</coordinate>

因为x和y是坐标的属性,不是在实际上描述XML,而是描述代表它的对象。


0

这里有一个设计XML语法的方法列表,非常棒。

正如上面所述,这是一种主观实践,但是该网站提供了一些有用的指导,例如“使用此模式解决问题X”……或者“优缺点是……”。


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