Google XML 文档格式风格指南
1.0 版本
版权所有 Google 2008
简介
本文档提供了一组通用指南,用于设计新的 XML 文档格式(并在某种程度上也适用于 XML 文档;请参阅第 11 节)。文档格式通常包括正式部分(DTD、模式)和用规范的英文散文表达的部分。
这些指南适用于新设计,并不旨在强制对现有设计进行追溯性更改。当参与公共或私有文档格式设计的创建时,这些指南可能有用,但不应控制小组的共识。
本指南适用于由机器生成和使用的 XML 设计,而不是由人读取的 XML 设计。其规则
不适用于 XHTML(应尽可能像 HTML 一样格式化)或 ODF 等旨在表达富文本的格式。包含 XHTML 或其他富文本格式嵌入内容的文档,但同时包含纯粹的机器可解释部分,对于机器可解释的部分
应遵循本风格指南。它也不影响通过从 Proto Buffers 转换或通过其他类型的格式创建的 XML 文档格式。
大多数指南都添加了简短的理由。它们保存在同一文档中,希望它们不会过时,但不被认为是规范性的。
本文档中的术语“必须”、“不得”、“应该”、“不应该”和“可以”按照
RFC 2119 的含义使用。
1. 设计还是不设计,这是个问题
- 尽可能尝试重用现有的 XML 格式,尤其是那些允许扩展的格式。创建全新的格式应谨慎考虑;首先阅读 Tim Bray 的警告。尽可能尝试获得对您格式的广泛审查,包括来自您组织外部的审查。[理由:新的文档格式是有成本的:它们必须经过用户审查、记录和学习。]
- 如果您正在重用或扩展现有格式,请合理使用规定的元素和属性,特别是任何必需的元素和属性。不要完全改变它们的用途,但如果普通的语义不合适,请尝试看看它们如何以创造性的方式使用。作为最后的手段,如果格式要求使用某个元素或属性,但该元素或属性不适合您的用例,请使用一些固定的字符串作为其值。[理由:标记重用是好的,标记滥用是坏的。]
- 扩展格式时,使用现有格式的隐式样式,即使它与本指南相矛盾。[理由:一致性。]
2. 模式
- 文档格式应该使用模式语言表达。[理由:清晰性和机器可检查性。]
- 模式语言应该是 RELAX NG 紧凑语法。嵌入式 Schematron 规则可以添加到模式中,以进行额外的精细控制。[理由:RELAX NG 是最灵活的模式语言,对设计几乎没有任意限制。紧凑语法非常容易阅读和学习,并且在必要时可以与 XML 语法进行一对一的转换。Schematron 可以很好地处理任意的跨元素和跨属性约束。]
- 模式应该使用 “香肠切片”样式(每个元素一条规则)。如果模式简短且简单,则模式可以使用 “俄罗斯套娃”样式(模式类似于文档)。“威尼斯百叶窗”样式(每个元素类型一条规则)不适合 RELAX NG,不应使用。
- 应提供正则表达式以帮助验证复杂的值。
- 可以提供 DTD 和/或 W3C XML 模式,以便与现有产品、工具或用户兼容。[理由:我们不能一下子改变世界。]
3. 命名空间
- 元素名称必须位于命名空间中,除非扩展不使用命名空间的预先存在的文档类型。应该使用默认命名空间。[理由:无命名空间的文档已经过时;每个名称集都应该位于某个命名空间中。使用默认命名空间可以提高可读性。]
- 除非属性名称来自外部文档类型或旨在用于外部文档类型,否则属性名称不应位于命名空间中。[理由:命名空间中的属性名称必须始终具有前缀,这很烦人且难以阅读。]
- 命名空间名称是 HTTP URI。命名空间名称应该采用 https://example.com/whatever/year 的形式,其中 whatever 是基于文档类型名称的唯一值,而 year 是命名空间创建的年份。在 year 之前可能还有其他的 URI 路径部分。[理由:现有约定。提供年份允许回收代码名称。]
- 除非特定元素或属性的语义发生了极不兼容的重大变化,否则不得更改命名空间。[理由:更改命名空间需要更改所有客户端代码。]
- 命名空间前缀应该简短(但不要太短,以至于可能与另一个项目冲突)。不得使用单字母前缀。前缀应该只包含小写 ASCII 字母。[理由:易于输入且不存在编码兼容性问题。]
4. 名称和枚举值
注意:“名称”是指元素、属性和枚举值的名称。
- 所有名称必须使用 lowerCamelCase。也就是说,它们以小写字母开头,然后名称中每个新单词都以大写字母开头。[理由:采用单一样式可以提供一致性,这有助于引用名称,因为大小写是已知的,因此不必记住。它匹配 Java 风格,其他语言可以使用自动名称转换来处理。]
- 名称必须只包含 ASCII 字母和数字。[理由:易于输入且不存在编码兼容性问题。]
- 名称不应超过 25 个字符。应通过设计简洁明了的名称来避免使用较长的名称。如果一个名称只有通过变得晦涩才能保持在这个限制范围内,则应该忽略该限制。[理由:较长的名称使用起来很笨拙,并且需要额外的带宽。]
- 发布的标准缩写,如果足够广为人知,可以用于构建名称。不得使用临时缩写。首字母缩略词必须被视为用于驼峰命名的单词:informationUri,而不是 informationURI。[理由:一个社区广为人知的缩写对于需要使用相同文档格式的其他社区来说通常是无法理解的(并且他们确实理解完整的名称);将首字母缩略词视为一个单词可以更容易地看到单词边界在哪里。]
5. 元素
- 所有元素必须包含以下之一:空、字符内容或子元素。不得使用混合内容。[理由:许多 XML 数据模型无法正确处理混合内容,并且其使用使元素顺序相关。与往常一样,文本格式不受此规则的约束。]
- 仅仅包装重复子元素的 XML 元素不应使用。[理由:它们在 Atom 中未使用,并且没有增加任何东西。]
6. 属性
- 文档格式不得依赖于开始标记中属性的顺序。[理由:很少有 XML 解析器报告该顺序,并且它不是 XML 信息集的一部分。]
- 元素不应重载太多属性(通常不超过 10 个)。相反,使用子元素来封装密切相关的属性。[理由:这种方法保持了 XML 使用元素提供的内置可扩展性,并且对于在规范演变时提供向前兼容性很有用。]
- 属性不得用于保存换行符有意义的值。[理由:这种换行符被符合 XML 标准的 XML 解析器转换为空格。]
- 文档格式必须允许在属性值周围使用单引号或双引号。[理由:XML 解析器不会报告差异。]
7. 值
- 数值应该是 32 位有符号整数、64 位有符号整数或 64 位 IEEE 双精度浮点数,所有数值都以 10 为底表示。这些分别对应于 XML Schema 类型 xsd:int、xsd:long 和 xsd:double。如果特定情况下需要,可以使用 xsd:integer(无限精度整数)值。[理由:XML Schema 中有太多数值类型:这些提供了一个合理的子集。]
- 不应使用布尔值(请改用枚举)。如果必须使用它们,它们必须表示为 true 或 false,对应于 XML Schema 类型 xsd:boolean 的子集。不得使用 xsd:boolean 的替代值 1 和 0。[理由:布尔参数不可扩展。允许数值的额外灵活性不会被任何解析器抽象掉。]
- 日期应使用 RFC 3339 格式表示,它是 ISO 8601 格式和 XML Schema xsd:dateTime 格式的一个子集。应该使用 UTC 时间而不是本地时间。[理由:存在太多的日期格式和时区,尽管人们认识到有时本地时间保留了重要的信息。]
- 不应在字符内容和属性值中使用嵌入式语法。值中的语法意味着 XML 工具在很大程度上是无用的。日期、URI 和 XPath 表达式等语法是例外。[理由:用户应该能够仅使用 XML 解析器来处理 XML 文档,而无需额外的专用解析器,这些解析器很容易出错。]
- 注意值中的空格。XML解析器不会去除元素中的空格,但会将属性中的换行符转换为空格。然而,应用程序框架可能会进行更激进的空格去除。你的文档格式*应该*给出空格去除的规则。
8. 键值对
- 简单的键值对*应该*使用一个空元素来表示,该元素的名称代表键,并且包含值的 value 属性。具有 value 属性的元素*可以*同时具有一个 unit 属性来指定测量值的单位。对于物理测量,*应该*使用国际单位制。[理由:简单性和设计一致性。将值保存在属性中会将其对用户隐藏,因为仅显示值而不显示键没有意义。]
- 如果可能的键的数量非常大或无界,则键值对*可以*用单个通用元素表示,该元素具有 key、value 以及可选的 unit 和 scheme 属性(用于区分来自不同域的键)。在这种情况下,还应提供(不一定在同一文档中)带有易于理解的解释的键列表。
9. 二进制数据
注意:对于是否应将二进制数据包含在XML文档中,没有严格的规定。如果数据太大,则最好链接到它。
- 二进制数据*必须*不能直接按原样包含在XML文档中,而*必须*使用Base64编码进行编码。[理由:XML不允许任意二进制字节。]
- 可以省略Base64所需的换行符。[理由:换行符的目的是使纯文本行保持简短,但XML实际上不是纯文本。]
- 可以将名为 xsi:type 且值为 xs:base64Binary 的属性附加到此元素,以指示正在使用Base64格式。[理由:不透明的blobs应附带解码说明。]
10. 处理指令
- 除了指定纯粹的本地处理约定外,*必须*不要创建新的处理指令,并且*应该*完全避免使用。*可以*使用现有的标准化处理指令。[理由:处理指令不太适合XML数据模型,并且始终可以用元素代替;它们的存在主要是为了避免破坏向后兼容性。]
11. XML文档实例的表示
注意:这些仅是指南,因为程序创建的实例的格式通常不受程序员的控制(例如,当使用XML序列化库时)。在任何情况下,XML解析器都不应依赖于这些指南。使用标准的XML解析器,而不是手动编写的hack。
- 使用的字符编码*应该*是UTF-8。例外情况需要极其令人信服的情况。[理由:UTF-8是通用的并且被广泛使用。]
- *应该*尽可能在文档的根元素中声明命名空间。[理由:清晰性和一致性。]
- 命名空间URI到前缀的映射在整个文档中*应该*保持不变,并且也*应该*在设计的文档中使用。[理由:清晰性和一致性。]
- 众所周知的前缀,例如 html:(用于XHTML),dc:(用于都柏林核心元数据)和xs:(用于XML Schema)*应该*用于标准命名空间。[理由:人类可读性。]
- *应该*不要在标签中使用冗余空格。在开始标记中的每个属性之前使用一个空格;如果开始标记太长,则*可以*用换行符代替空格。[理由:一致性和简洁性。]
- 空元素*可以*表示为空标签或紧随其后的开始标签。任何应用程序都不应对这两种格式进行区分。[理由:XML解析器不会区分它们。]
- *可以*使用2个空格缩进子元素来很好地打印文档。包含字符内容的元素*应该*不换行。可以在除最后一个属性值之外的任何属性值之后使用换行符(可能带有额外的缩进)来中断较长的开始标记。[理由:与我们的样式的一般兼容性。包装字符内容会影响其值。]
- 属性值*可以*用引号或撇号引起来。规范*必须*不要求或禁止使用任何一种形式。' 和 " *可以*自由地用于转义每种类型的引号。[理由:没有XML解析器报告此区别。]
- *必须*不要使用注释来携带真实数据。注释*可以*用于包含手写XML中的TODO。在公开传输的文档中*应该*根本不使用注释。[理由:注释通常被解析器丢弃。]
- 如果仍然使用注释,则它们*应该*仅出现在文档序言或包含子元素的元素中。如果需要漂亮的打印,请像元素一样漂亮地打印注释,但要换行。注释*应该*不要出现在包含字符内容的元素中。[理由:注释中和周围的空格可以提高可读性,但是将注释嵌入到字符内容中可能会导致对内容中的空格的混淆。]
- 注释*应该*在 <!-- 之后和 --> 之前有空格。[理由:可读性。]
- *可以*使用CDATA节;它们等效于使用 & 和 <。规范*必须*不要求或禁止使用CDATA节。[理由:很少有XML解析器报告此区别,并且CDATA和文本的组合通常也报告为单个对象。]
- *必须*不使用XML标准实体引用 &, <, >, ", 和 ' 之外的实体引用。*可以*使用字符引用,但除非字符编码不是UTF-8,否则首选实际字符。与往常一样,文本格式不受此规则的约束。
12. 元素 vs. 属性
注意:对于决定何时使用属性以及何时使用元素,没有严格的规定。以下是设计人员应考虑的一些因素;没有给出理由。
12.1. 一般要点
-
属性比元素更严格,并且所有设计都包含一些元素,因此全元素设计最简单 - 这与最佳设计不同。
-
在树样式数据模型中,元素通常在内部表示为节点,该节点比用于表示属性的字符串使用更多的内存。有时节点是不同的应用程序特定类,这在许多语言中也需要占用内存来表示这些类。
-
流式传输时,元素一次处理一个(甚至可能是一块一块地处理,具体取决于您使用的XML解析器),而元素的所有属性及其值会一次性报告,这会花费内存,尤其是在某些属性值很长的情况下。
-
元素内容和属性值都需要进行适当的转义,因此转义不应成为设计中的考虑因素。
-
在某些编程语言和库中,处理元素更容易;而在另一些语言和库中,处理属性更容易。谨防将易于处理用作标准。特别是,XSLT可以用相同的工具处理它们。
-
如果通常应向用户显示一条数据,请考虑使用元素;否则,请考虑使用属性。(由于某种原因,通常会违反此规则。)
-
如果要扩展现有模式,请通过类比该模式中的完成方式来完成操作。
-
合理的模式语言,即RELAX NG和Schematron,对称地处理元素和属性。较旧和较粗糙的 模式语言,例如DTD和XML Schema,往往对元素有更好的支持。
12.2 使用元素
-
如果某件事可能在数据模型中出现多次,请使用元素,而不是引入名称类似于 foo1, foo2, foo3 .... 的属性。
-
使用元素来表示可以被视为独立对象的一条信息,并且当该信息通过父/子关系与另一条信息相关时。
-
当数据包含严格的类型或关系规则时,请使用元素。
-
如果在两个数据之间顺序很重要,请对它们使用元素:属性本质上是无序的。
-
如果一条数据具有或可能具有其自身的子结构,请在元素中使用它:将子结构放入属性总是很麻烦的。同样,如果数据是某个更大的数据的组成部分,请将其放入元素中。
-
前一个规则的例外:多个以空格分隔的标记可以安全地放入属性中。原则上,分隔符可以是任何东西,但是模式语言验证器当前只能处理空格,因此最好坚持使用它。
-
如果一条数据跨越多行,请使用元素:XML解析器会将属性值中的换行符更改为空格。
- 如果一条数据非常大,请使用元素,以便可以流式传输其内容。
-
如果一条数据使用自然语言,请将其放入元素中,以便可以使用 xml:lang 属性来标记所使用的语言。某些类型的自然语言文本,例如日语,通常使用 注释,这些注释通常使用子元素表示;从右到左的语言(如希伯来语和阿拉伯语)可能同样需要子元素来正确管理 双向性。
12.3 使用属性
-
如果数据是来自枚举、代码列表或受控词汇表中的代码,请尽可能将其放入属性中。例如,语言标记、货币代码、医疗诊断代码等最好作为属性处理。
-
如果一条数据实际上是另一条数据的元数据(例如,表示主要数据所服务的类或角色,或者指定处理方法),请尽可能将其放入属性中。
-
特别是,如果一条数据是另一条数据的ID,或者是对这种ID的引用,请将标识片放入属性中。当它是ID时,请使用名称 xml:id 作为属性。
-
超文本引用通常放在 href 属性中。
-
如果一条数据适用于一个元素及其任何后代元素,除非在其中一些元素中被覆盖,否则通常将其放入属性中。众所周知的例子是 xml:lang、xml:space、xml:base 和命名空间声明。
-
如果简洁性确实是最重要的事情,请使用属性,但请考虑使用 gzip 压缩 - 它在具有高度重复结构的文档上效果很好。
13. 结束语
运用常识并保持一致。为可扩展性设计。你肯定会需要它。[理由:长期而痛苦的经验。]
在设计XML格式时,花几分钟时间查看其他格式并确定其样式。制定样式指南的目的是使人们可以专注于您所说的内容,而不是您所说的方式。
如果死板地遵守这些规则会导致粗糙、随意、令人厌恶的设计,那么可以打破任何或所有这些规则(是的,甚至包括那些说必须遵守的规则)。特别地,属性和子元素的随机混合很难理解和使用,尽管当数据明显分为两组(例如简单/复杂或元数据/数据)时,同时使用两者通常是有意义的。
新手总是问
“元素还是属性?
哪个对我最好?”
知者如狮吼;
智者笑如虎。
--一首短歌,或加长版的俳句
[待办事项:如果建立了模式注册表,请添加指向它的链接]