XML属性是附着在元素开始标签上的名称值对,用来为元素提供附加信息。很多初学者在写XML时常常纠结:一段数据到底应该放在属性里,还是拆成子元素?属性值用什么引号?特殊字符怎么处理?这些问题看似简单,却直接影响文档的可读性、可扩展性和后续解析的难易程度。本文将从基础语法入手,逐步展开属性的使用规则、约束定义以及实践中的选择技巧,帮助你建立一套完整的XML属性知识体系。

一、XML属性的基础语法
XML属性必须写在元素的开始标签中,形式为属性名="属性值"。属性名和属性值之间用等号连接,属性值必须用引号包裹起来。同一个元素内不能出现两个同名的属性,否则文档就不是格式良好的XML,解析器会直接报错。
下面是一个简单的例子,展示了属性的几种常见写法:
<person id="1001" sex="male">
<name>张三</name>
<age>25</age>
</person>
在这个例子中,id和sex是person元素的属性,而name和age是它的子元素。属性与子元素并存是完全合法的,关键在于语义上的划分。属性值可以使用双引号也可以使用单引号,例如sex='male'同样合法。如果属性值本身包含双引号,就可以改用单引号来包裹,反之亦然。
需要注意的是,XML属性名区分大小写,ID和id是两个不同的属性。属性名只能以字母或下划线开头,不能以数字开头,也不能包含空格。属性值中不能直接包含<、&等特殊字符,遇到这些字符必须使用实体引用进行转义。
二、属性与子元素的选择策略
同样的信息既可以用属性表示,也可以用子元素表示,这是XML设计中最经典的争议点之一。比如下面两种写法表达的信息完全一样:
<!-- 方式一:使用属性 -->
<student name="李四" age="20" major="计算机科学"/>
<!-- 方式二:使用子元素 -->
<student>
<name>李四</name>
<age>20</age>
<major>计算机科学</major>
</student>
那么到底该怎么选?业界一般有以下几条经验法则。第一,元数据适合用属性,数据本身适合用子元素。比如id、版本号、创建时间这类描述性信息用属性,而姓名、地址这类业务数据用子元素。第二,如果数据可能包含多值或者需要进一步分层,必须用子元素,因为属性只能存单个字符串值。第三,如果数据内容较长,或者包含需要转义的特殊字符较多,用子元素可读性更好。第四,属性无法被解析器以树形结构展开,某些XPath或XSLT操作对属性的支持不如元素灵活。
一个典型的判断标准是:问自己这段数据是不是元素的标识或描述信息。如果是,用属性;如果是元素承载的实际内容,用子元素。HTML中大量使用属性(如<a>元素的href属性),这体现了属性适合表达元数据的特点。
三、属性值的转义与CDATA处理
属性值中出现的特殊字符需要转义处理。XML预定义了五个实体引用:<表示小于号,&表示与符号,>表示大于号,"表示双引号,'表示单引号。在属性值中使用这些实体引用,解析器会自动将其还原为原始字符。
下面演示一个包含特殊字符的属性写法:
<note author="张三 & 李四" condition="5 < 10"/>
解析后author属性的值是"张三 & 李四",condition属性的值是"5 < 10"。需要强调的是,属性值中不能包含裸露的与符号,即使它看起来像实体引用的开始也不行,必须完整转义。这一点和元素文本内容不同,元素文本可以通过CDATA区段避免转义,但属性值中没有CDATA的用法,只能老老实实用实体引用。另外,属性值中的换行符、制表符等空白字符在解析时会被规范化处理,连续空白可能被压缩为单个空格,这一点在传输需要保留格式的数据时要格外小心。
四、在DTD和Schema中约束属性
定义XML文档结构时,可以在DTD或XML Schema中对属性进行约束声明。DTD中使用ATTLIST声明属性,可以指定属性的类型和默认值规则。常见的属性类型包括CDATA(普通字符数据)、ID(唯一标识符)、IDREF(引用其他ID)、ENTITY等。默认值规则有四种:REQUIRED表示必须提供,IMPLIED表示可选,FIXED表示值固定,直接给值则表示默认值。
<!ELEMENT person (name, age)>
<!ATTLIST person
id ID #REQUIRED
sex CDATA "unknown"
version CDATA #FIXED "1.0"
>
上面的DTD规定person元素的id属性必须提供且全文档唯一,sex属性可省略,省略时默认为unknown,version属性如果出现则必须是1.0。相比DTD,XML Schema提供了更强的类型约束,可以限定属性为整数、日期、枚举值等具体类型,还能通过use属性控制出现要求:
<xs:attribute name="age" type="xs:integer" use="required"/>
<xs:attribute name="sex" use="optional" default="unknown">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="male"/>
<xs:enumeration value="female"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
Schema中type为xs:integer的属性会强制校验值必须是整数,枚举限制则保证值只能取指定集合中的某一项,这种细粒度的控制在DTD中是做不到的。实际项目中如果对数据校验要求较高,推荐使用Schema;如果只是简单的结构约束,DTD更轻量。
五、常见错误与规范化建议
初学者写属性时最容易犯这几类错误:一是忘记给属性值加引号,这是从HTML转过来最常见的习惯问题;二是在同一元素上写了重复属性名;三是属性值中直接使用与符号没有转义;四是把复杂的多值数据硬塞进一个属性里,比如用逗号拼接多个值。虽然语法上可行,但解析和扩展都很麻烦,遇到这种情况应该拆成子元素列表。
规范化的建议是:先在团队内约定属性的使用范围,通常限定在标识、类型标记、简单配置参数这类场景;数据结构较复杂时优先用子元素;对外提供接口的XML文档建议配上Schema约束并发布,让调用方能提前校验。掌握这些原则后,再动手写XML就不会在属性和元素之间摇摆不定了。