我曾经使用过一个颇受欢迎的 XML 命令行工具,因为我注意到它有一个选项可以把目录内容列表保存为 XML 文档。我好奇地试了试。看到的结果如清单 1 所示。
清单 1. 原来的格式
<xml> <d p="rwxrwxrwx" a="2005.07.26 15:23:13" m="2005.07.26 15:21:23" s="170" n="."/> <d p="rwxrwxrwx" a="2005.07.26 15:21:30" m="2005.06.10 13:49:59" s="2448" n=".."/> <f p="rw-r--r--" a="2005.07.26 15:20:46" m="2005.06.07 14:00:55" s="6148" n=".DS_Store"/> <f p="rw-r--r--" a="2005.07.26 15:21:30" m="2005.07.26 15:21:24" s="800" n="canonthunderbirdplist.xml"/> <f p="rw-r--r--" a="2005.07.26 15:21:30" m="2005.07.26 15:20:46" s="945" n="thunderbirdplist.xml"/> </xml> |
这种格式至少有三种不同的错误,虽然不是致命的但都很重要。这是设计 XML 格式一个经典的反例,但是稍加调整就能使其更容易使用,更加健壮。
第一个问题是根元素的名称。字符串 “xml” 被 XML 规范保留,除了万维网联盟(W3C)外,任何人都不能在任何传播的格式中使用。W3C 可能在将来的规范中重定义该名称(以及所有其他以 x、m、l 或者任何组合方式开始的名称)。这并不是一种结构良好性错误,但是有些解析器读到该文档时仍然会发出警告。
这个名称的第二个问题是它不具有描述性。当然,它确实是 XML。如果包含 XML 声明这一点会更明显。根元素的名称应该反映文档的类型。比如对该元素来说 DirectoryListing
就是不错的选择。
第二个主要的问题是对元素和属性使用缩写名。f
是什么?还有 p
、a
、m
和 s
又是什么?我能猜出来,因为我知道这些数据来自哪里,但是对于别人来说就不那么清楚了。最好把全名拼出来,如清单 2 所示。
清单 2. 完整的名称
<DirectoryListing> <Directory permissions="rwxrwxrwx" size="170" name="." lastAccessed="2005.07.26 15:23:13" lastModified="2005.07.26 15:21:23"/> <Directory permissions="rwxrwxrwx" size="2448" name=".." lastAccessed="2005.07.26 15:21:30" lastModified="2005.06.10 13:49:59"/> <File permissions="rw-r--r--" size="6148" name=".DS_Store" lastAccessed="2005.07.26 15:20:46" lastModified="2005.06.07 14:00:55"/> <File permissions="rw-r--r--" size="800" name="canonthunderbirdplist.xml" lastAccessed="2005.07.26 15:21:30" lastModified="2005.07.26 15:21:24"/> <File permissions="rw-r--r--" size="945" name="thunderbirdplist.xml" lastAccessed="2005.07.26 15:21:30" lastModified="2005.07.26 15:20:46"/> </DirectoryListing> |
是不是这样清楚得多?虽然稍微长了点,但是不存在问题了。如果文件大小很重要(虽然很少如此),那么还有减少磁盘和内存占用而不会导致含糊不清的方法。
最后一个问题可能最严重。属性包含大量不能通过 XML 解析器访问的子结构。解析此文档的每个程序都必须包含自定义的解析器,用于解析权限格式和时间格式。为什么不让 XML 解析器来完成这些困难的工作呢?这些属性应该放到包含必要的子结构的子元素中。比如,可以这样设计 permissions
子元素:
<permissions> <world> <read>true</read> <write>false</write> <execute>false</execute> </world> <group> <read>true</read> <write>true</write> <execute>false</execute> </group> <owner> <read>true</read> <write>true</write> <execute>false</execute> </owner> </permissions> |
如果愿意的话也可使用属性,但是应该把权限分别放在单独的属性中:
<File size="945" name="thunderbirdplist.xml" lastAccessed="2005.07.26 15:21:30" lastModified="2005.07.26 15:20:46" worldread="true" worldwrite="false" worldexecute="false" groupread="true" groupwrite="true" groupexecute="false" ownerread="true" ownerwrite="true" ownerexecute="false" /> |
至少有十几种不同的方式,但是无论哪种方法都应该通过 XML 标记使结构明确清晰,不要隐含在文本字符串中。XML 解析器的目的是为客户机应用程序提供单独的信息片段,而不是要求客户机程序再进一步分解内容。
类似地,可以对时间信息作如下分解:
<lastModified> <year>2005</year> <month>06</month> <day>10</day> <hour>13</hour> <minute>49</minute> <second>59</second> </lastModified> |
在某种程度上,时间都是统一的,因此保留无区分的字符串可能也行。但是,字符串格式应该进一步标准化,以便很容易用模式语言验证,或者作为各种日期时间库的输入,如 java.util.Date
。ISO 8601 时间格式就很好:
lastModified="2005-06-10T13:49:59" |
如果将来需要,ISO 8601 还定义了表示时区和小数秒的语法。
清单 3 显示了文档的最终版本。
清单 3. 改进的格式
<?xml version="1.0" encoding="UTF-8"?> <DirectoryListing> <Directory size="170" name="." lastAccessed="20050726T15:23:13" lastModified="20050726T15:21:23"> <Permissions> <world> <read>true</read> <write>true</write> <execute>true</execute> </world> <group> <read>true</read> <write>true</write> <execute>true</execute> </group> <owner> <read>true</read> <write>true</write> <execute>true</execute> </owner> </Permissions> </Directory> <Directory size="2448" name=".." lastAccessed="20050726T15:21:30" lastModified="20050610T13:49:59"> <Permissions> <world> <read>true</read> <write>true</write> <execute>true</execute> </world> <group> <read>true</read> <write>true</write> <execute>true</execute> </group> <owner> <read>true</read> <write>true</write> <execute>true</execute> </owner> </Permissions> </Directory> <File size="6148" name=".DS_Store" lastAccessed="20050726T15:20:46" lastModified="20050607T14:00:55"> <Permissions> <world> <read>true</read> <write>false</write> <execute>false</execute> </world> <group> <read>true</read> <write>false</write> <execute>false</execute> </group> <owner> <read>true</read> <write>true</write> <execute>false</execute> </owner> </Permissions> </File> <File size="800" name="canonthunderbirdplist.xml" lastAccessed="20050726T15:21:30" lastModified="20050726T15:21:24"> <Permissions> <world> <read>true</read> <write>false</write> <execute>false</execute> </world> <group> <read>true</read> <write>false</write> <execute>false</execute> </group> <owner> <read>true</read> <write>true</write> <execute>false</execute> </owner> </Permissions> </File> <File size="945" name="thunderbirdplist.xml" lastAccessed="20050726T15:21:30" lastModified="20050726T15:20:46"> <Permissions> <world> <read>true</read> <write>false</write> <execute>false</execute> </world> <group> <read>true</read> <write>false</write> <execute>false</execute> </group> <owner> <read>true</read> <write>true</write> <execute>false</execute> </owner> </Permissions> </File> </DirectoryListing> |
虽然长了些,但是更清晰,也更便于处理。不要害怕冗长。如果需要更短、更简单的格式,可以使用 XSLT 样式表转换。但是,取出额外的结构比放入它要简单得多。
缩写名称和含糊的字符串格式是只有 32K 内存、速度数千赫兹的超级计算机时代的遗留问题。已经不属于这个时代了。设计 XML 格式的时候,应该强调清晰和准确而不是紧凑。设计应针对易理解性和可维护性,而不是榨出每个字节。要方便那些处理您的文档的每个人,也包括您自己。
如果要将一个函数或变量定义为全局的函数或者变量,则可以先考虑将其定义为静态变量,这样可以防止外部文件调用该函数和变量。