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

时间:2008-10-23 21:12:17

标签: xml xsd

作为业余软件开发人员(我还在学术界),我为XML文档编写了一些模式。我经常遇到导致丑陋的XML文档的设计漏洞,因为我不完全确定XML的语义究竟是什么。

我的假设:

<property> value </property>

property = value

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

具有特殊描述符的属性,即属性。

<parent>
  <child> value </child>
</parent>

父母有一个特征“孩子”,其值为“值”。

<tag />

“Tag”是一个标志或直接转换为文本。我不确定这个。

<parent>
  <child />
</parent>

“孩子”描述“父母”。 “child”是一个标志或布尔值。我也不确定这个。

如果你想做一些代表笛卡尔坐标的事情,就会出现歧义:

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

<coordinate> 0,1 </coordinate>

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

哪一个最正确?基于我目前对XML模式设计的概念,我倾向于第三种,但我真的不知道。

有哪些资源简洁地描述了如何有效地设计xml架构?

14 个答案:

答案 0 :(得分:24)

一个通用(但很重要!)建议永远不要在单个节点(无论是文本节点还是属性节点)中存储多个逻辑数据片段。否则,您最终需要自己的解析逻辑 您通常从框架中免费获得的XML解析逻辑。

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

但是<coordinate> 0,1 </coordinate>不是很好,因为它在一个XML节点中存储两个逻辑数据片段(X坐标和Y坐标) - 迫使消费者解析数据他们的XML解析器外部。虽然用逗号分割字符串非常简单,但仍然存在一些含糊之处,例如如果最后有一个额外的逗号会发生什么。

答案 1 :(得分:17)

答案 2 :(得分:11)

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

虽然我们正在讨论架构设计的主题,但我会在我的首选(最大)重用(元素和类型)方面加上我的两分钱,这也可以促进这些元素的外部“逻辑”引用例如,存储在数据库中的数据字典中的实体。

请注意,虽然“伊甸园”模式模式提供了最大程度的重用,但它也涉及最多的工作。在这篇文章的底部,我提供了博客系列中涵盖的其他模式的链接。

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

通过全局定义所有元素来使用模块化方法,并且像Venetian Blind方法一样,所有类型定义都是全局声明的。每个元素全局定义为节点的直接子节点,其type属性可以设置为指定的复杂类型之一。

<?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·非·I·CAL     与数学有关。 (等式,坐标等)         “以最简单或标准的形式”         http://dictionary.reference.com/browse/canonical

其他,更容易构建,但更少可恢复/更“非规范化/冗余”架构模式包括

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

模式只有一个全局元素 - 根元素。所有其他元素和类型逐渐深入嵌套,因为每个类型都适合上面的类型,因此给它起名称。由于此设计中的元素是在本地声明的,因此无法通过import或include语句重复使用。

Salami Slice方法 http://blogs.msdn.com/skaufman/archive/2005/04/25/411809.aspx

所有元素都是全局定义的,但类型定义是在本地定义的。这样,其他模式可以重用这些元素。使用此方法,具有本地定义类型的全局元素提供元素内容的完整描述。这个信息'slice'是单独声明的,然后汇总在一起,也可以拼凑在一起构建其他模式。

威尼斯盲人方法 http://blogs.msdn.com/skaufman/archive/2005/04/29/413491.aspx

与俄罗斯玩偶方法类似,它们都使用单一的全局元素。 Venetian Blind方法通过全局命名和定义所有类型定义来描述模块化方法(与Salami Slice方法相反,该方法在全局声明元素并在本地类型化)。每个全局定义的类型描述单个“板条”,并且可以由其他组件重用。此外,所有本地声明的元素都可以是命名空间限定或命名空间不合格(板条可以“打开”或“关闭”),具体取决于模式顶部的elementFormDefault属性设置。

答案 3 :(得分: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

答案 4 :(得分:3)

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

在我看来,对XML的一个重要见解是它不是一种语言:它是一种语法。每个文档模型都是一种单独的语言。

不同的文化都会以自己特殊的方式使用XML。即使在W3C规范中,您也可以在XML Schema的camelCaseNames中以XSLT的短划线名称和Java中嗅到Lisp。同样,不同的应用程序域将调用不同的XML习语。

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

更多面向对象的文档模型(如SVG)很少或根本不使用文本节点,而只使用元素和属性。

我对文档模型设计的个人经验法则是这样的:

  • 如果是需要mixed content的免费标签汤,请使用HTML和DocBook作为灵感来源。其他规则只是相关的。
  • 如果某个值是复合值或分层值,请使用元素。 XML数据不需要进一步解析,除了已建立的习惯用法,例如IDREFS,它们是简单的空格分隔序列。
  • 如果值可能需要多次出现,请使用元素。
  • 如果某个值可能需要进一步细化或稍后丰富,请使用元素。
  • 如果某个值显然是原子的(布尔值,数字,日期,标识符,简单标签),并且最多可能出现一次,那么请使用属性。

另一种说法可能是:

  • 如果它是叙述性的,它不是面向对象的。
  • 如果它是面向对象的,则将对象建模为元素,将原子属性建模为属性。
编辑:有些人似乎完全放弃了属性。没有什么错误的,但是我不喜欢它,因为它会使文档膨胀并使它们不必手动阅读和写入。

答案 5 :(得分: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的同时为架构制作附录将会更加困难。

答案 6 :(得分:1)

在我们的Java项目中,我们经常使用JAXB来自动解析XML并将其转换为对象结构。我猜其他语言你会有类似的东西。合适的生成器可以使用您选择的编程语言自动创建对象结构。这使得XML的处理通常更容易,同时仍然具有用于系统之间通信的可移植XML表示。

如果你确实使用了这样的自动映射,你会发现这对模式有很大的限制 - 除非你想在翻译中做特殊的魔术,否则<coordinate> <x> 0 </x> <y> 1 </y> </coordinate>是要走的路。您最终会得到一个类Coordinate,其中包含两个属性xy,其中包含模式中声明的相应类型。

答案 7 :(得分:1)

我被指定编写一堆XML模式来将我的公司系统与我们的客户集成。十多年前我设计了十几个,并且发现规范中的许多扩展功能在实践中都不能很好地工作。在设计新的实践之前,我已经搜索了当前的最佳实践(并且到了这里!)。

上面的一些提示很有用,但我不喜欢几乎所有的参考资料。我找到的最好的设计建议来自微软。

最佳参考是XML Schema Design Patterns: Avoiding Complexity。在这里你会发现这个理智的建议:

  

似乎很多架构作者都会得到最好的服务   理解和利用有效的特征子集   由W3C XML Schema提供,而不是试图理解所有   语言的深奥和细枝。

并详细解释以下指南:

  • 为什么要使用全局和本地元素声明
  • 为什么要使用全局和本地属性声明
  • 为什么你应该理解XML命名空间如何影响W3C XML Schema
  • 为什么你应该始终将elementFormDefault设置为“qualified”
  • 为什么要使用属性组
  • 为什么要使用模型组
  • 为什么要使用内置的简单类型
  • 为什么要使用复杂类型
  • 为什么不应该使用符号声明
  • 为什么要小心使用替代组
  • 为什么你应该使用key / keyref / unique over ID / IDREF来识别身份约束
  • 为什么你应该仔细使用变色龙图式
  • 为什么不应使用默认值或固定值,尤其是xs类型:QName
  • 为什么要使用简单类型的限制和扩展
  • 为什么要使用复杂类型的扩展
  • 为什么要小心使用复杂类型的限制
  • 为什么要小心使用抽象类型
  • 使用通配符提供明确定义的可扩展性点
  • 请勿使用群组或类型重新定义

我对他们的建议的建议是,当他们说“小心使用”时,你应该简单地避免它。我的印象是Schema规范不是由软件开发人员编写的。他们试图使用一些面向对象的概念,但却搞得一团糟。很多扩展机制都是无用的或非常冗长的。我真的不明白某人是如何发明复杂类型的限制的。

本网站还有两篇不错的文章:

一个普遍的提示是使用与官方规范不同的方式指定您的模式。放松NG看起来是最受青睐的规范语言。遗憾的是,您将失去标准化的最佳功能之一。

答案 8 :(得分:0)

查看您尝试表示的数据的关系是我找到的最佳方法。

答案 9 :(得分:0)

我经常发现自己在同样的问题上挣扎,但我发现在实践中它并不重要,xml只是数据。

那就是说,我通常更喜欢“如果它说明节点是一个属性,那么它就是一个子节点”。

在你的例子中我会去:

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

因为x和y是坐标的属性,实际上并没有说出关于xml的任何内容,而是关于它所代表的对象。

答案 10 :(得分:0)

我想,这取决于结构的复杂程度或简单程度 我会将x和y作为属性,除非x和y有自己的详细信息

您可以查看HTML或任何其他形式的标记,用于定义事物(在WPF情况下为XAML,在闪存情况下为MXML)以了解为什么选择某些内容作为针对子节点的属性<) / p>

如果不重复x和y,它们可以是属性。

让我们说坐标有多个x和y,我猜xml不允许一个节点具有相同名称的多个属性。在这种情况下,您将不得不使用子节点。

答案 11 :(得分:0)

对于您想要表示的每个值,使用元素或子元素没有任何内在错误。

主要考虑因素是有时使用属性更清晰。由于元素只能具有给定名称的一个属性,因此您将使用1:1的基数。如果您将数据表示为子元素,则可以使用您想要的基数(或者稍后将其扩展)。

Rob Wells上面的回答是正确的:这取决于你想要建模的关系。

任何时候显然永远不会只有1:1的关系,属性可能更清晰。

答案 12 :(得分:0)

Here是设计XML语法的绝佳方法列表。

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

答案 13 :(得分:0)

在处理笛卡尔坐标时,我发现属性形式更易于管理。我的项目往往需要多个名称空间,并且在名称空间之间共享坐标类型定义在子元素形式中变得丑陋。在子元素形式中,您必须限定子元素,在基础元素或根元素上使用juggle名称空间,或者默认为非限定元素名称(即namespace hiding