题
在工作中,我们被要求创建 XML 文件以将数据传递到另一个离线应用程序,然后该应用程序将创建第二个 XML 文件来传回,以便更新我们的一些数据。在此过程中,我们一直在与其他应用程序的团队讨论 XML 文件的结构。
我想出的示例基本上是这样的:
<INVENTORY>
<ITEM serialNumber="something" location="something" barcode="something">
<TYPE modelNumber="something" vendor="something"/>
</ITEM>
</INVENTORY>
另一个团队表示这不是行业标准,属性只能用于元数据。他们建议:
<INVENTORY>
<ITEM>
<SERIALNUMBER>something</SERIALNUMBER>
<LOCATION>something</LOCATION>
<BARCODE>something</BARCODE>
<TYPE>
<MODELNUMBER>something</MODELNUMBER>
<VENDOR>something</VENDOR>
</TYPE>
</ITEM>
</INVENTORY>
我建议第一个的原因是创建的文件的大小要小得多。传输期间文件中将包含大约 80000 个项目。事实证明,他们的建议比我的建议大三倍。我搜索了提到的神秘的“行业标准”,但我能找到的最接近的是 XML 属性只能用于元数据,但说争论的焦点是什么是真正的元数据。
经过冗长的解释(抱歉)之后,您如何确定什么是元数据?在设计 XML 文档的结构时,您应该如何决定何时使用属性或元素?
解决方案
我使用这个经验法则:
- 属性是独立的东西,即颜色、ID、名称。
- 元素是具有或可能具有自己的属性或包含其他元素的东西。
所以你的很接近。我会做类似的事情:
编辑: :根据下面的反馈更新了原始示例。
<ITEM serialNumber="something">
<BARCODE encoding="Code39">something</BARCODE>
<LOCATION>XYX</LOCATION>
<TYPE modelNumber="something">
<VENDOR>YYZ</VENDOR>
</TYPE>
</ITEM>
其他提示
属性的一些问题是:
- 属性不能包含多个值(子元素可以)
- 属性不易扩展(以供将来更改)
- 属性不能描述结构(子元素可以)
- 属性更难通过程序代码操作
- 属性值不容易根据 DTD 进行测试
如果使用属性作为数据容器,最终会得到难以阅读和维护的文档。尝试使用元素来描述数据。仅使用属性来提供与数据无关的信息。
不要像这样结束(这不是 XML 应该使用的方式):
<note day="12" month="11" year="2002"
to="Tove" to2="John" from="Jani" heading="Reminder"
body="Don't forget me this weekend!">
</note>
“XML”代表“可扩展 标记 语言”。标记语言意味着数据是文本, 已标记 包含有关结构或格式的元数据。
XHTML 是按其预期方式使用 XML 的一个示例:
<p><span lang="es">El Jefe</span> insists that you
<em class="urgent">MUST</em> complete your project by Friday.</p>
在这里,元素和属性之间的区别很明显。文本元素显示在浏览器中,属性是有关的说明 如何 显示它们(尽管有一些标签不能以这种方式工作)。
当 XML 不是用作标记语言而是用作 数据序列化 语言中,“数据”和“元数据”之间的区别更加模糊。因此,元素和属性之间的选择或多或少是任意的,除了以下内容: 不能 用属性来表示(参见 feenster 的回答)。
XML 元素与 XML 属性
XML 就是关于协议的。 首先遵循社区或行业中任何现有的 XML 模式或既定约定。
如果您确实需要从头开始定义模式,那么以下是一些一般注意事项,应该为您提供参考: 元素与属性决策:
<versus>
<element attribute="Meta content">
Content
</element>
<element attribute="Flat">
<parent>
<child>Hierarchical</child>
</parent>
</element>
<element attribute="Unordered">
<ol>
<li>Has</li>
<li>order</li>
</ol>
</element>
<element attribute="Must copy to reuse">
Can reference to re-use
</element>
<element attribute="For software">
For humans
</element>
<element attribute="Extreme use leads to micro-parsing">
Extreme use leads to document bloat
</element>
<element attribute="Unique names">
Unique or non-unique names
</element>
<element attribute="SAX parse: read first">
SAX parse: read later
</element>
<element attribute="DTD: default value">
DTD: no default value
</element>
</versus>
这可能取决于您的使用情况。用于表示从数据库生成的结构化数据的 XML 可以很好地与最终作为属性放置的字段值配合使用。
然而,用作消息传输的 XML 通常使用更多元素会更好。
例如,假设我们有答案中建议的 XML:-
<INVENTORY>
<ITEM serialNumber="something" barcode="something">
<Location>XYX</LOCATION>
<TYPE modelNumber="something">
<VENDOR>YYZ</VENDOR>
</TYPE>
</ITEM>
</INVENTORY>
现在我们想要将 ITEM 元素发送到设备以打印条形码,但是可以选择编码类型。我们如何表示所需的编码类型?突然,我们意识到,有点迟了,条形码不是一个单一的自动值,而是它可能符合打印时所需的编码。
<ITEM serialNumber="something">
<barcode encoding="Code39">something</barcode>
<Location>XYX</LOCATION>
<TYPE modelNumber="something">
<VENDOR>YYZ</VENDOR>
</TYPE>
</ITEM>
要点是,除非您构建某种 XSD 或 DTD 以及命名空间来固定结构,否则最好保留您的选择。
IMO XML 当可以在不破坏现有代码的情况下进行调整时,它是最有用的。
我在模式设计中使用以下关于属性与属性的指南。要素:
- 使用长期运行的元素(通常是字符串或标准化类型的元素)
- 如果有两个值分组(例如元素的 eventStartDate 和 eventEndDate)。在上一个示例中,“事件”应该有一个新元素,该元素可能包含起点和端代属性。
- 营业日期、日期时间和数字(例如计数,数量和费率)应为要素。
- 非企业的时间元素,例如上次更新,到期应为属性。
- 非业务数字(例如哈希码和索引)应该是属性。* 如果类型很复杂,请使用元素。
- 如果值是简单类型且不重复,则使用属性。
- xml:id 和 xml:lang 必须是引用 XML 模式的属性
- 在技术上可行的情况下优先选择属性。
对属性的偏好是它提供以下内容:
- 唯一(该属性不能多次出现)
- 顺序并不重要
- 上述属性是可继承的(这是“所有”内容模型在当前模式语言中不支持的)
- 好处是它们不那么冗长,占用的带宽也更少,但这并不是更喜欢属性而不是元素的真正原因。
我添加了 当技术上可行时 因为有时无法使用属性。例如,属性集选择。例如,当前模式语言无法使用 (startDate 和 endDate) xor (startTS 和 endTS)
如果 XML 模式开始允许限制或扩展“所有”内容模型,那么我可能会放弃它
这个问题没有通用的答案(我积极参与了 W3C 规范的创建)。XML 可用于多种用途 - 类似文本的文档、数据和声明性代码是最常见的三种用途。我也经常使用它作为数据模型。在这些应用程序的某些方面,属性更为常见,而在其他方面,子元素则更为自然。各种工具的一些功能也使它们的使用变得更容易或更困难。
XHTML 是属性具有自然用途的领域(例如在类='foo')。属性没有顺序,这可能使某些人更容易开发工具。如果没有模式,OTOH 属性就很难键入。我还发现命名空间属性 (foo:bar="zork") 在各种工具集中通常更难管理。但看看一些 W3C 语言,您就会发现常见的混合情况。SVG、XSLT、XSD、MathML 是一些著名语言的示例,并且都具有丰富的属性和元素。有些语言甚至允许不止一种方法来做到这一点,例如
<foo title="bar"/>;
或者
<foo>
<title>bar</title>;
</foo>;
请注意,这些在语法上并不等效,需要处理工具的明确支持)
我的建议是查看最接近您的应用程序的领域的常见做法,并考虑您可能希望应用哪些工具集。
最后确保区分命名空间和属性。一些 XML 系统(例如Linq)将命名空间表示为 API 中的属性。在我看来,这很丑陋并且可能令人困惑。
有疑问时, 吻 -- 当您没有明确的理由使用属性时,为什么要混合使用属性和元素。如果您稍后决定定义 XSD,那么最终也会变得更干净。然后,如果您稍后决定从 XSD 生成类结构,那也会更简单。
百万美元的问题!
首先,现在不要太担心性能。您将会惊讶于优化的 xml 解析器解析您的 xml 的速度有多快。更重要的是,你对未来的设计是什么:随着 XML 的发展,您将如何保持松散耦合和互操作性?
更具体地说,您可以使元素的内容模型更加复杂,但扩展属性会更困难。
使用元素作为数据,使用属性作为元数据(有关元素数据的数据)。
如果某个元素在您选择的字符串中显示为谓词,则有一个很好的迹象表明它应该是一个属性。同样,如果一个属性从未用作谓词,那么它可能不是有用的元数据。
请记住,XML 应该是机器可读的,而不是人类可读的,并且对于大型文档,XML 压缩得非常好。
其他人已经介绍了如何区分属性和元素,但从更一般的角度来看,将所有内容都放在属性中是错误的,因为这会使生成的 XML 更小。
XML 并不是为了紧凑而设计的,而是为了可移植和人类可读的。如果您想减少传输中数据的大小,请使用其他方法(例如 谷歌的协议缓冲区).
无论哪种方式都是有争议的,但您的同事是正确的,因为 XML 应该用于“标记”或围绕实际数据的元数据。就您而言,您是对的,因为在使用 XML 进行域建模时,有时很难确定元数据和数据之间的界线在哪里。在实践中,我所做的就是假装标记中的任何内容都是隐藏的,并且只有标记之外的数据是可读的。从这个意义上说,这份文件有意义吗?
XML 的体积是出了名的大。对于运输和存储,如果您有足够的处理能力,强烈建议您进行压缩。由于其重复性,XML 的压缩效果很好,有时甚至非常好。我已经将大文件压缩到原始大小的 5% 以下。
支持您立场的另一点是,当其他团队争论风格时(因为大多数 XML 工具将像处理全#PCDATA 文档一样轻松地处理全属性文档),您正在争论实用性。虽然风格不能完全忽视,但技术优点应该更重要。
这两种存储对象属性的方法都是完全有效的。你应该脱离务实的考虑。尝试回答以下问题:
- 哪种表示形式可以加快数据解析\生成速度?
- 哪种表示形式可以加快数据传输速度?
可读性重要吗?
...
这很大程度上是一个偏好问题。我尽可能使用元素进行分组并使用数据属性,因为我认为这比替代方案更紧凑。
比如我比较喜欢......
<?xml version="1.0" encoding="utf-8"?>
<data>
<people>
<person name="Rory" surname="Becker" age="30" />
<person name="Travis" surname="Illig" age="32" />
<person name="Scott" surname="Hanselman" age="34" />
</people>
</data>
...代替....
<?xml version="1.0" encoding="utf-8"?>
<data>
<people>
<person>
<name>Rory</name>
<surname>Becker</surname>
<age>30</age>
</person>
<person>
<name>Travis</name>
<surname>Illig</surname>
<age>32</age>
</person>
<person>
<name>Scott</name>
<surname>Hanselman</surname>
<age>34</age>
</person>
</people>
</data>
但是,如果我的数据在 20-30 个字符内不容易表示,或者包含许多引号或其他需要转义的字符,那么我会说是时候分解这些元素了......可能与 CData 块一起。
<?xml version="1.0" encoding="utf-8"?>
<data>
<people>
<person name="Rory" surname="Becker" age="30" >
<comment>A programmer whose interested in all sorts of misc stuff. His Blog can be found at http://rorybecker.blogspot.com and he's on twitter as @RoryBecker</comment>
</person>
<person name="Travis" surname="Illig" age="32" >
<comment>A cool guy for who has helped me out with all sorts of SVn information</comment>
</person>
<person name="Scott" surname="Hanselman" age="34" >
<comment>Scott works for MS and has a great podcast available at http://www.hanselminutes.com </comment>
</person>
</people>
</data>
利用我们来之不易的面向对象直觉怎么样?我通常发现可以直接思考哪个是对象、哪个是该对象的属性或它引用哪个对象。
任何直观上有意义的对象都应该适合作为元素。它的属性(或属性)将是 xml 中这些元素的属性或具有属性的子元素。
我认为对于更简单的情况,例如示例中的面向对象类比,可以确定哪个是元素,哪个是元素的属性。
只是对一些错误信息的一些更正:
@约翰·巴林格:属性可以包含任何字符数据。< > & " ' 需要转义为 <>&”和 ', 分别。如果您使用 XML 库,它会为您处理好这些事情。
天哪,如果您确实需要,属性可以包含图像等二进制数据,只需对其进行 base64 编码并将其设为数据即可:网址。
@费恩斯特:在 IDS 或 NAMES 的情况下,属性可以包含以空格分隔的多个项目,其中包括数字。挑剔,但这最终可以节省空间。
使用属性可以使 XML 与 JSON 保持竞争。看 脂肪标记:消除一次一卡路里的脂肪标记神话.
我总是对这类讨论的结果感到惊讶。对我来说,有一个非常简单的规则来决定数据是否属于属性或内容,那就是数据是否具有可导航的子结构。
例如,非标记文本始终属于属性。总是。
列表属于子结构或内容。随着时间的推移,可能包含嵌入的结构化子内容的文本属于内容。(根据我的经验,当使用 XML 进行数据存储或交换时,这种情况相对较少 - 带标记的文本。)
以这种方式编写的 XML 模式非常简洁。
每当我看到类似的案例 <car><make>Ford</make><color>Red</color></car>
, ,我心里想“哎呀,作者是否认为 make 元素中会有子元素?” <car make="Ford" color="Red" />
明显更具可读性,毫无疑问如何处理空格等。
只要给出空白处理规则,我相信这是 XML 设计者的明确意图。
这在 HTML 中非常清楚,可以清楚地看到属性和标记的差异:
- 所有数据都在标记之间
- 属性用于表征该数据(例如格式)
如果您只有 XML 形式的纯数据,则差异不太明显。数据可以位于标记之间或作为属性。
=> 大多数数据应该位于标记之间。
如果你想在这里使用属性:您可以将数据分为两类:数据和“元数据”,其中元数据不是您想要呈现的记录的一部分,而是诸如“格式版本”、“创建日期”等内容。
<customer format="">
<name></name>
...
</customer>
也可以说:“使用属性来表征标签,使用标签来提供数据本身。”
我同意芬斯特的观点。如果可以的话,远离属性。元素是演化友好的,并且 Web 服务工具包之间的互操作性更强。您永远不会发现这些工具包使用属性序列化您的请求/响应消息。这也是有道理的,因为我们的消息是 Web 服务工具包的数据(而不是元数据)。
随着时间的推移,属性很容易变得难以管理,相信我。我个人总是远离他们。元素更加明确,并且解析器和用户都可以读取/使用。
我唯一一次使用它们是定义资产 URL 的文件扩展名:
<image type="gif">wank.jpg</image> ...etc etc
我想如果你100%知道该属性不需要扩展你就可以使用它们,但是你知道多少次。
<image>
<url>wank.jpg</url>
<fileType>gif</fileType>
</image>