【问题标题】:What are best practices for designing XML schemas? [closed]设计 XML 模式的最佳实践是什么? [关闭]
【发布时间】:2010-09-18 22:33:29
【问题描述】:

作为一名业余软件开发人员(我还在学术界),我为 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 模式?

【问题讨论】:

  • 很好的问题,遗憾的是没有明确的答案 :) 但至少我知道没有人在乎,所以我可以继续随机设计我的模式 :)

标签: xml xsd


【解决方案1】:

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

【讨论】:

  • 我的问题不是什么关系映射到什么其他关系。我的问题是什么关系应该映射到 xml 的每个语法单元,如标签和属性。
  • 但是我的回答是基于您应该知道数据中实体的关系这一事实。例如,每个用户名一个电子邮件地址。
【解决方案2】:

XML 在设计方面有些主观——我认为元素和属性的布局方式并没有确切的指导方针,但我倾向于使用元素来表示“事物”并使用属性来表示单数它们的属性/属性。

就坐标示例而言,两者都是完全可以接受的,但我倾向于使用&lt;coordinate x="" y=""/&gt;,因为它更简洁,并且如果你有很多这样的文档,会使文档更具可读性。

不过,最重要的是模式的命名空间。确保 (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 只是数据。

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

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

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

    因为 x 和 y 是坐标的属性,实际上并没有说明 xml,而是说明了它所代表的对象。

    【讨论】:

      【解决方案4】:

      在设计基于 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 写入旧模式的同时对模式进行补充将会更加困难。

      【讨论】:

        【解决方案5】:

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

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

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

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

        【讨论】:

          【解决方案6】:

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

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

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

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

          【讨论】:

            【解决方案7】:

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

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

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

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

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

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

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

            另一种说法是:

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

            编辑:有些人似乎喜欢完全放弃属性。它并没有错误,但我不喜欢它,因为它会使文档变得臃肿,并且使它们变得不必要难以手写。

            【讨论】:

              【解决方案8】:

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

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

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

              【讨论】:

                【解决方案9】:

                我同意以下 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 语句重用。

                Salami Slice 方法 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 可以“打开”或“关闭”)。

                【讨论】:

                  【解决方案10】:

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

                  如果您确实使用了这样的自动映射,您会发现这会极大地限制模式 - &lt;coordinate&gt; &lt;x&gt; 0 &lt;/x&gt; &lt;y&gt; 1 &lt;/y&gt; &lt;/coordinate&gt; 是要走的路,除非您想在翻译中使用特殊的魔法。您最终将得到一个具有两个属性 xy 的类 Coordinate,并具有架构中声明的适当类型。

                  【讨论】:

                    【解决方案11】:

                    【讨论】:

                    • +1 对 Roger 的网站表示赞同。 xFront 上有很多很好的 XML 和 XML Schema 内容。
                    • @james.garriss,是的。 Roger 不断突破在 XML Schema、XPath 和 XSLT 中教授最高级主题的界限。
                    【解决方案12】:

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

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

                    【讨论】:

                      【解决方案13】:

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

                      【讨论】:

                        【解决方案14】:

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

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

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

                        似乎许多模式作者最好由 理解和利用特征的有效子集 由 W3C XML Schema 提供,而不是试图理解所有 语言的深奥和细节。

                        并详细解释以下准则:

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

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

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

                        一个普遍的技巧是用不同于官方规范的东西来指定你的模式。 Relax NG 看起来是最受青睐的规范语言。不幸的是,您将失去它的最佳功能之一,即标准化。

                        【讨论】:

                          猜你喜欢
                          • 2020-03-08
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多