XML Schema 数据类型:从基础到实战约束
数据类型这件事,说白了就是给数据画个圈。你告诉系统:「这个字段只能填数字,那个字段最多20个字符」。系统就老老实实帮你守着。我在项目中见过太多因为类型定义太松导致的数据污染——金额字段里混进文字,日期格式五花八门。嗯,今天我们就把这个圈画清楚。
内置数据类型:开箱即用的工具箱
XML Schema 给我们准备了一套相当齐全的内置类型。我个人习惯把它们分成三组:
| 类别 | 常用类型 | 典型场景 |
|---|---|---|
| 字符串类 | string, normalizedString, | 名称、描述、代码 |
| 数值类 | int, decimal, double, positiveInteger | 数量、金额、百分比 |
| 时间类 | date, time, dateTime, duration | 订单日期、操作时间 |
自定义简单类型:给数据加把锁
内置类型够用吗?说实话,大部分时候不够。业务上总有各种奇奇怪怪的约束——「年龄必须在0到150之间」「订单状态只能是这几个值」。这时候就需要我们自己动手了。
自定义简单类型通过 restriction 来实现。看个例子:
<xs:simpleType name="ageType">
<xs:restriction base="xs:integer">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="150"/>
</xs:restriction>
</xs:simpleType>
这个类型就保证了年龄只能是0到150之间的整数。超出范围?Schema 直接报错,根本进不了系统。
枚举约束:把选项关进笼子
枚举是我用得最多的约束之一。为什么?因为业务上「固定选项」的场景太多了——性别、状态、分类、级别……
我曾经在一个电商项目中,订单状态字段用了 string 类型,结果数据里出现了「已发货」「已发贷」「已发」三种写法。嗯,你猜怎么着?都是同一个意思。从那以后,所有枚举字段我都强制用 Schema 约束。
<xs:simpleType name="orderStatusType">
<xs:restriction base="xs:string">
<xs:enumeration value="待支付"/>
<xs:enumeration value="已支付"/>
<xs:enumeration value="已发货"/>
<xs:enumeration value="已完成"/>
<xs:enumeration value="已取消"/>
</xs:restriction>
</xs:simpleType>
这样写的好处很明显:
- 数据一致性:不会出现同义不同词的情况
- 可读性强:枚举值本身就是业务含义
- 扩展方便:加一个值就行,不影响已有数据
模式约束:正则表达式的威力
有些约束用枚举搞不定,比如手机号、邮箱、邮政编码。这些有固定格式但值不固定的场景,就得靠 pattern 了。
模式约束本质上就是正则表达式。我个人觉得这是 Schema 里最强大也最容易翻车的地方。看个手机号的例子:
<xs:simpleType name="phoneType">
<xs:restriction base="xs:string">
<xs:pattern value="1[3-9]\d{9}"/>
</xs:restriction>
</xs:simpleType>
这个模式的意思是:以1开头,第二位是3到9,后面跟9位数字。一共11位。简单明了。
约束组合:把多个条件叠起来
实际项目中,一个字段往往需要多个约束。比如价格字段:必须是正数,最多两位小数,不能超过10000。这时候可以把多个 facet 组合使用:
<xs:simpleType name="priceType">
<xs:restriction base="xs:decimal">
<xs:minExclusive value="0"/>
<xs:maxInclusive value="10000"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
这个类型同时保证了:
- 大于0(不能免费或负数)
- 不超过10000
- 最多两位小数
你想想看,如果不用 Schema 约束,这些逻辑得写在多少地方?前端要写、后端要写、数据库可能还要写。现在一个类型定义就搞定了。
知识体系总览
下面这张图把今天讲的内容串起来了。我建议你保存下来,写 Schema 的时候对照着看:
实战建议
最后分享几个我在项目中积累的经验:
- 先定义后使用:把常用的自定义类型放在独立的 Schema 文件里,方便复用。我一般会建一个
common-types.xsd,里面放项目通用的类型定义。 - 约束宁严勿松:刚开始可以严格一点,后面再放宽。反过来就麻烦了——数据已经进去了,再收紧约束会导致验证失败。
- 枚举值用代码管理:如果枚举值经常变动,建议和代码里的枚举类保持同步。我见过一个项目,Schema 里枚举值和代码里对不上,结果数据校验通过了,业务逻辑却报错了。
- 正则一定要测试:写好的 pattern 拿几组真实数据跑一遍。正则是出了名的「看着对,跑起来错」,别问我怎么知道的。
union 可以把多个简单类型合并。比如一个字段既可以是数字 ID,也可以是字符串 "unknown"。这在数据迁移阶段特别有用。