XML Schema 1.1とXerces,Saxon

2012年4月5日にXML Schema 1.1はW3Cの勧告になりました.見逃すことができないのが、XSD1.1で新たに加わったAssertionの機能です.この機能を使うとXPath 2.0で制約条件を記述してコンテンツを調べ、満たさない場合はAssertメッセージを出すことができます.もう勧告が出てもうすぐ一年になろうとしていますが、ApacheのパーサーのXercesがこの機能をインプリメントしています.
 
How Xerces-J uses an XPath 2.0 engine for XML Schema 1.1 assertions and type alternatives?
http://xerces.apache.org/xerces2-j/faq-xs.html#faq-2
 
そして検証エンジンとしてXercesやSaxonを使用しているoXygen 14.2 XMLエディタがこの機能を使えるようにして先日リリースされました.この紹介ビデオが以下にあります.簡単ですが使い方をデモしています.Assertionのセクションです.
 
 
私もoXygen 14.2を落としたので早速やってみました.サンプルはDITAではなくoXygenのサンプルフォルダにあるpersonal.xsdとpersonal-schema.xmlです.
 
このスキーマでは、person要素を定義しています.person要素ですのでname要素があり、これはfamily(姓)要素とgiven(名前)要素を持ちます.このperson要素にcountry属性を加えてみました.そして@country="JP"のとき、givenは2文字以上という条件を記述してみます.このような感じになります.
 
<xs:assert test="string-length(name/given) ge 2"/>
 
イメージ 1

これだと
 
    <person id="s.abe" photo="personal-images/s.abe.jpg" country="JP">
        <name>
            <given>しんちゃん</given>
            <family>安倍</family>
        </name>
        <email>shin@abc.co.jp</email>
        <link manager="harris.anderson"/>
        <url href="http://www.example.com/na/abe.html"/>
    </person>
 
のようなデータはvalidですが、
 
    <given>進</given>
 
と変えると
 
System ID: D:\My Documents\Proj\XML-Schema\personal-schema.xml
Main validation file: D:\My Documents\Proj\XML-Schema\personal-schema.xml
Scenario name: personal-schema
Schema: D:\My Documents\Proj\XML-Schema\personal.xsd
Engine name: Xerces
Severity: error
Description: cvc-assertion: Assertion evaluation ('string-length(name/given) ge 2') for element 'person' on schema type '#AnonType_person' did not succeed.
Start location: 60:6
End location: 60:71
URL: http://www.w3.org/TR/xmlschema11-1/#cvc-assertion
 
のエラーとなります.
これはスゴイです!何故かというと、XPathを書けるので、今までのXML Schemaではできないコンテンツの相互関係の制約条件を記述することができるからです.すばらしい機能です.
 
例えば、姓と名を一発でチェックしたければ次のようにも書けてしまいます.
 
<xs:assert test="every $e in name/* satisfies string-length($e) ge 2"/>
 
で調子に乗って、今度は名前の字類条件をやってみようとしました.例えば、country="JP"だったら、漢字かひらがなの文字、country="US","UK"だったら、Basic Latinの文字ならOKとするものです.
 
<xs:assert test="if (@country = ('US','UK')) then matches(name/given,'\p{IsBasicLatin}') else true()"/>
<xs:assert test="if (@country = ('JP')) then matches(name/given,'\p{IsCJKUnifiedIdeographs}|\p{IsHiragana}') else true()"/>
 
ところがこれが地雷でした.まず上記のXPath式はXercesではエラーとなります.
 
System ID: D:\My Documents\Proj\XML-Schema\personal-schema.xml
Main validation file: D:\My Documents\Proj\XML-Schema\personal-schema.xml
Scenario name: personal-schema
Schema: D:\My Documents\Proj\XML-Schema\personal.xsd
Engine name: Xerces
Severity: error
Description: cvc-assertion: Assertion evaluation ('if (@country = ('US','UK')) then matches(name/given,'\p{IsBasicLatin}') else true()') for element 'person' on schema type '#AnonType_person' did not succeed. FORX0002 - Invalid regular expression. Unknown character property name {BasicLatin} near index 15
\p{IsBasicLatin}
               ^.
Start location: 6:6
End location: 6:91
URL: http://www.w3.org/TR/xmlschema11-1/#cvc-assertion
 
つまりXercesの使っているXPathエンジンでは、'\p{IsBasicLatin}'という正規表現を通してくれないのです.仕方がないので、Saxonに切り替えてみました、するとXPath式のエラーはなくなりますが、
 
<name>
    <given>進Z</given>
    <family>安倍</family>
</name>
 
のようなエラーとみなすべきデータもvalidで通ってしまうのです.
 
見てみると正規表現はいろいろエンジンによってバリエーションがあるようです.
Not all Unicode regex engines use the same syntax to match Unicode blocks.
http://www.regular-expressions.info/unicode.html
 
正規表現のエンジンによってスキーマXPath正規表現を変えねばならないというのはちょっと苦しいです.まあ現時点では標準的なXPath構文を使うにとどめるべきなのでしょう.それでも有用なことに変わりありません.DITAでもXML Schemaでの特殊化で大いに活用できるのではないかと思います.