XSD 对没有分隔符的值进行日期时间验证
XSD DateTime validation on value with no separators
我正在研究一个 XSD 模式来验证一个 XML 文件,其中包含客户从他们的系统导出的数据。
它们从系统中导出 dates/datetimes 的方式是不带任何分隔符输出它们,例如20150323151728(格式:YYYYMMDDhhmmss)
有没有办法在我的架构中输入该格式以使 XML 通过验证?
可能的解决方案有这三种。
验证前重新格式化
一种可能更简单的方法是在数据源和验证器之间插入一个 SAX 过滤器,该过滤器查找日期时间值并根据 ISO 8601 规则重新格式化它们,以便它们可以根据 xsd:dateTime 进行验证。 (注意闰秒。)
Michael Kay 在他的回答中称此为 tranform-before-validating;他是对的,这是一个有用的设计模式。
更干净的导出
根据客户的不同,可能还值得建议在导出到 XML 时,他们会以 ISO 8601 格式导出日期时间戳。这在技术层面上要么简单,要么非常简单;当然,在政治或组织层面上,这要么简单,要么绝对不可能。您可能比 Stack Overflow 的任何 reader 更能判断这有多实用;如果你还不知道答案,问这个问题可能会提供信息(但也可能是危险的,所以这里没有说你应该问)。
日期时间的正则表达式
可以使用正则表达式来描述一组公历日期(以及类似的一组公历日期时间),尽管正则表达式往往相当麻烦并且手工推导容易出错。如果您可以使用布尔运算符(特别是否定),那就更容易了:合法日期的集合是 (a) 日期为 1 到 30 的所有日期的集合,加上 (b) 包含日期的所有日期的集合月份是 1 或 3 或 5 或 7 或 8 或 10 或 12 并且月份的日期是 31,减去 (c) 月份是 2 并且月份的日期是 29 或 30 的所有日期的集合,加上(d) 年份为闰年但不能被 100 整除,月份为 2,日期为 29 的所有日期的集合。
在XSD1.1中,条件类型赋值可以用来获得布尔否定的效果,尽管先赢类型赋值规则会强制你把否定条件放在第一位(如果文字匹配模式 $bogus-february-29 或 $other-bogus-february,然后将元素绑定到 xsd:error,否则 ...)。
后记
实际上,即使没有否定也可以很简单地做到这一点。为清楚起见,我将模式的时间和年份部分分解为一般实体,声明为
<!ENTITY hh "([01][0-9]|2[0-3])">
<!ENTITY mm "([05][0-9])">
<!ENTITY ss "([05][0-9])">
<!ENTITY midnight "240000">
<!ENTITY tod "(&hh;&mm;&ss;|&midnight;)">
<!ENTITY yyyy "(([1-9][0-9]*)?[0-9]{4})">
<!ENTITY leapyear "([1-9][0-9]*)?[0-9]{2}(0[48]|[2468][048]|[13579][26])">
现在你想要的类型是这样的:
<simpleType name="YYYYMMDDhhmmss">
<annotation>
<documentation>
<p xmlns="http://www.w3.org/1999/xhtml">
A date-time stamp in the form YYYYMMDDhhmmss.
</
</
</
<restriction base="string">
<pattern value="&yyyy;(0[0-9]|1[0-2])(0[1-9]|1[0-9]|2[0-8])&tod;">
<annotation>
<documentation>Days 01-28 of any month.</
</
</
<pattern value="&yyyy;(0[469]|11)(29|30)&tod;">
<annotation>
<documentation>29-30 of April, June, September, November.</
</
</
<pattern value="&yyyy;(0[13578]|1[02])(29|30|31)">
<annotation>
<documentation>29-31 of January, March, May, July,
August, October, December.</documentation>
</annotation>
</pattern>
<pattern value="&leapyear;0229&tod;">
<annotation>
<documentation>29 February.</
</
</
</
</
一个经常被忽视的选项是验证前转换设计模式:不是在 XML 到达时对其进行验证,而是在验证之前将其转换为 "cleaner"。在这种情况下,"cleaner" 表示以 XSD 期望的格式使用 ISO dates/times。
我正在研究一个 XSD 模式来验证一个 XML 文件,其中包含客户从他们的系统导出的数据。
它们从系统中导出 dates/datetimes 的方式是不带任何分隔符输出它们,例如20150323151728(格式:YYYYMMDDhhmmss)
有没有办法在我的架构中输入该格式以使 XML 通过验证?
可能的解决方案有这三种。
验证前重新格式化
一种可能更简单的方法是在数据源和验证器之间插入一个 SAX 过滤器,该过滤器查找日期时间值并根据 ISO 8601 规则重新格式化它们,以便它们可以根据 xsd:dateTime 进行验证。 (注意闰秒。)
Michael Kay 在他的回答中称此为 tranform-before-validating;他是对的,这是一个有用的设计模式。
更干净的导出
根据客户的不同,可能还值得建议在导出到 XML 时,他们会以 ISO 8601 格式导出日期时间戳。这在技术层面上要么简单,要么非常简单;当然,在政治或组织层面上,这要么简单,要么绝对不可能。您可能比 Stack Overflow 的任何 reader 更能判断这有多实用;如果你还不知道答案,问这个问题可能会提供信息(但也可能是危险的,所以这里没有说你应该问)。
日期时间的正则表达式
可以使用正则表达式来描述一组公历日期(以及类似的一组公历日期时间),尽管正则表达式往往相当麻烦并且手工推导容易出错。如果您可以使用布尔运算符(特别是否定),那就更容易了:合法日期的集合是 (a) 日期为 1 到 30 的所有日期的集合,加上 (b) 包含日期的所有日期的集合月份是 1 或 3 或 5 或 7 或 8 或 10 或 12 并且月份的日期是 31,减去 (c) 月份是 2 并且月份的日期是 29 或 30 的所有日期的集合,加上(d) 年份为闰年但不能被 100 整除,月份为 2,日期为 29 的所有日期的集合。
在XSD1.1中,条件类型赋值可以用来获得布尔否定的效果,尽管先赢类型赋值规则会强制你把否定条件放在第一位(如果文字匹配模式 $bogus-february-29 或 $other-bogus-february,然后将元素绑定到 xsd:error,否则 ...)。
后记
实际上,即使没有否定也可以很简单地做到这一点。为清楚起见,我将模式的时间和年份部分分解为一般实体,声明为
<!ENTITY hh "([01][0-9]|2[0-3])">
<!ENTITY mm "([05][0-9])">
<!ENTITY ss "([05][0-9])">
<!ENTITY midnight "240000">
<!ENTITY tod "(&hh;&mm;&ss;|&midnight;)">
<!ENTITY yyyy "(([1-9][0-9]*)?[0-9]{4})">
<!ENTITY leapyear "([1-9][0-9]*)?[0-9]{2}(0[48]|[2468][048]|[13579][26])">
现在你想要的类型是这样的:
<simpleType name="YYYYMMDDhhmmss">
<annotation>
<documentation>
<p xmlns="http://www.w3.org/1999/xhtml">
A date-time stamp in the form YYYYMMDDhhmmss.
</
</
</
<restriction base="string">
<pattern value="&yyyy;(0[0-9]|1[0-2])(0[1-9]|1[0-9]|2[0-8])&tod;">
<annotation>
<documentation>Days 01-28 of any month.</
</
</
<pattern value="&yyyy;(0[469]|11)(29|30)&tod;">
<annotation>
<documentation>29-30 of April, June, September, November.</
</
</
<pattern value="&yyyy;(0[13578]|1[02])(29|30|31)">
<annotation>
<documentation>29-31 of January, March, May, July,
August, October, December.</documentation>
</annotation>
</pattern>
<pattern value="&leapyear;0229&tod;">
<annotation>
<documentation>29 February.</
</
</
</
</
一个经常被忽视的选项是验证前转换设计模式:不是在 XML 到达时对其进行验证,而是在验证之前将其转换为 "cleaner"。在这种情况下,"cleaner" 表示以 XSD 期望的格式使用 ISO dates/times。