43

作为一名业余软件开发人员(我还在学术界),我已经为 XML 文档编写了一些模式。我经常遇到导致 XML 文档难看的设计错误,因为我不完全确定 XML 的语义到底是什么。

我的假设:

<property> value </property>

属性=值

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

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

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

父母有一个特征“孩子”,它的价值是“价值”。

<tag />

“标签”是一个标志或直接翻译成文本。我不确定这个。

<parent>
  <child />
</parent>

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

如果您想做诸如表示笛卡尔坐标之类的事情,则会出现歧义:

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

<coordinate> 0,1 </coordinate>

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

这些选项中哪一个最正确?根据我目前对 XML 模式设计的概念,我会倾向于第三种,但我真的不知道。

有哪些资源可以简洁地描述如何有效地设计 xml 模式?

4

14 回答 14

25

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

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

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

于 2008-10-23T21:52:31.073 回答
17

看教程:

我还推荐:

于 2008-11-18T04:13:32.557 回答
11

我同意以下 cdragon 的建议以避免选项 #2。#1 和 #3 之间的选择很大程度上取决于风格。我喜欢将属性用于我认为是实体的属性,而将元素用于我认为是数据的东西。有时,很难分类。尽管如此,两者都不是“错误的”。

当我们讨论模式设计的主题时,我将添加我的两分钱,关于我喜欢的(最大)重用级别(元素和类型),这也可以促进这些实体的外部“逻辑”引用,比如说,存储在数据库中的数据字典。

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

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

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

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

该模式有一个单一的全局元素 - 根元素。所有其他元素和类型都嵌套得越来越深,因为每种类型都适合它上面的类型,所以给它起了名字。由于此设计中的元素是在本地声明的,因此它们不能通过 import 或 include 语句重用。

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

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

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

与俄罗斯娃娃方法类似,它们都使用单个全局元素。Venetian Blind 方法通过全局命名和定义所有类型定义来描述模块化方法(与全局声明元素和本地类型的 Salami Slice 方法相反)。每个全局定义的类型都描述了一个单独的“slat”,并且可以被其他组件重用。此外,根据架构顶部的 elementFormDefault 属性设置,所有本地声明的元素都可以是命名空间限定的或命名空间非限定的(slat 可以“打开”或“关闭”)。

于 2008-10-24T02:36:27.600 回答
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
于 2008-10-23T21:26:36.680 回答
3

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

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

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

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

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

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

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

另一种说法可能是:

  • 如果它是叙述性的,它就不是面向对象的。
  • 如果它是面向对象的,则将对象建模为元素,将原子属性建模为属性。

编辑:有些人似乎喜欢完全放弃属性。它没有任何问题,但我不喜欢它,因为它会使文档膨胀,并且使它们变得不必要,难以手动读写。

于 2008-10-23T21:46:10.853 回答
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 的同时对模式进行补充将更加困难。

于 2008-10-23T21:39:46.357 回答
1

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

如果您确实使用了这样的自动映射,您会发现这会极大地限制模式 -<coordinate> <x> 0 </x> <y> 1 </y> </coordinate>除非您想在翻译中使用特殊魔法,否则这是可行的方法。您最终将得到一个Coordinate具有两个属性xy模式中声明的适当类型的类。

于 2008-11-15T17:46:37.113 回答
1

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

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

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

似乎许多模式作者最好通过理解和利用 W3C XML 模式提供的功能的一个有效子集来获得最佳服务,而不是试图理解该语言的所有深奥和细节。

并详细解释以下准则:

  • 为什么应该使用全局和局部元素声明
  • 为什么应该使用全局和局部属性声明
  • 为什么您应该了解 XML 名称空间如何影响 W3C XML Schema
  • 为什么您应该始终将 elementFormDefault 设置为“合格”
  • 为什么应该使用属性组
  • 为什么你应该使用模型组
  • 为什么你应该使用内置的简单类型
  • 为什么应该使用复杂类型
  • 为什么不应该使用符号声明
  • 为什么要谨慎使用替代组
  • 为什么你应该更喜欢 key/keyref/unique 而不是 ID/IDREF 来进行身份约束
  • 为什么你应该小心使用变色龙模式
  • 为什么不应该使用默认值或固定值,尤其是对于 xs:QName 类型
  • 为什么应该使用简单类型的限制和扩展
  • 为什么应该使用复杂类型的扩展
  • 为什么要谨慎使用复杂类型的限制
  • 为什么要谨慎使用抽象类型
  • 使用通配符来提供明确定义的可扩展点
  • 不要使用组或类型重新定义

我对他们的建议的建议是,当他们说“谨慎使用”时,你应该避免它。我的印象是 Schema 规范不是由软件开发人员编写的。他们试图使用一些面向对象的概念,但做得一团糟。许多扩展机制是无用的或极其冗长。我真的不明白有人怎么能发明复杂类型的限制。

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

And one tip that is pervasive is to specify your schemas with something different than the official specification. Relax NG looks the most favored specification language. Unfortunately you will loose one of the best features of it that is the standardization.

于 2017-03-15T18:44:11.787 回答
0

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

于 2008-10-23T21:19:44.930 回答
0

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

也就是说,我通常更喜欢“如果它说明了节点的某些内容,则它是一个属性,否则它是一个子节点”方法。

在您的示例中,我会选择:

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

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

于 2008-10-23T21:28:27.020 回答
0

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

您可以查看用于定义事物的 HTML 或任何其他形式的标记(在 WPF 的情况下为 XAML,在 Flash 的情况下为 MXML)以了解为什么选择某事物作为属性而不是子节点)

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

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

于 2008-10-23T21:40:24.063 回答
0

为您想要表示的每个值使用元素或子元素本身并没有错。

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

Rob Wells 的上述回答是正确的:这取决于您尝试建模的关系。

任何时候,除了 1:1 的关系,显然永远不会有任何东西,一个属性可能会更清晰。

于 2008-10-23T21:40:40.490 回答
0

是设计 XML 语法的方法的一个很好的列表。

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

于 2009-09-28T11:32:58.820 回答
0

在处理笛卡尔坐标时,我发现属性形式更易于管理。我的项目往往需要多个命名空间,并且在命名空间之间共享坐标类型定义在子元素形式中变得很难看。在子元素表单中,您必须限定子元素,在基本或根元素上处理命名空间,或者默认为不限定的元素名称(即命名空间隐藏

于 2012-01-20T20:19:52.390 回答