今回はUnion Typesの話題です.Union Typeつまり共用体という型というと昔のC言語のunionを思い出します.XMLではどうなのでしょうか?
XMLでも共用体はXML Schemaで表すことができます.ある要素のコンテンツや属性が複数の型から成ることは十分ありえるのです.例えばXML Schemaのunion型の例でWebによく出てくるのはジーンズのサイズを表す以下のようなスキーマです.
xml version="1.0" encoding="UTF-8"
<xsschema xmlnsxs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xselement name="jeans_size" type="jeansType">
</xselement>
<xssimpleType name="jeansType">
<xsunion memberTypes="sizebyno sizebystring" />
</xssimpleType>
<xssimpleType name="sizebyno">
<xsrestriction base="xs:positiveInteger">
<xsmaxInclusive value="42"/>
</xsrestriction>
</xssimpleType>
<xssimpleType name="sizebystring">
<xsrestriction base="xs:string">
<xsenumeration value="small"/>
<xsenumeration value="medium"/>
<xsenumeration value="large"/>
</xsrestriction>
</xssimpleType>
</xsschema>
これにより、ジーンズのサイズは42(インチ?)までの正の整数、もしくは"small", "medium", "large"のいずれかの文字列と表現できます.このXML Schemeを参照するXMLインスタンスは例えば次のように書けるでしょう.
xml version="1.0" encoding="UTF-8"
<jeans_size
xmlnsxsi="http://www.w3.org/2001/XMLSchema-instance"
xsinoNamespaceSchemaLocation="./xsd/union-sample.xsd">42</jeans_size>
これを処理するXSLTスタイルシートの例は以下のようになります.
xml version="1.0" encoding="UTF-8"
<xslstylesheet xmlnsxsl="http://www.w3.org/1999/XSL/Transform"
xmlnsxs="http://www.w3.org/2001/XMLSchema"
xmlnsmath="http://www.w3.org/2005/xpath-functions/math"
xmlnsahf="http://www.antennahouse.com/names/function"
xmlnssaxon="http://saxon.sf.net/"
exclude-result-prefixes="xs math"
version="3.0">
<xsltemplate match="jeans_size">
<xslchoose>
<xslwhen test="data() instance of xs:positiveInteger">
<xslmessage select="'Numeric jeans size = ' || (data() treat as xs:positiveInteger) => string()"/>
</xslwhen>
<xslwhen test="data() instance of xs:string">
<xslmessage select="'Enumerated jeans size = ' || data()"/>
</xslwhen>
<xslotherwise>
<xslmessage select="'Invalid jeans size type'"/>
</xslotherwise>
</xslchoose>
</xsltemplate>
</xslstylesheet>
data()関数はSchema Awareな変換をやるときでないとめったに使用することはありません.引数に一般的にはノードを取って、それに型付けられた値を返してくれます.省略すればカレントコンテキストになります.(この場合はjeans_size要素)
これで先ほどのXMLを処理すれば
Numeric jeans size = 42
と表示されます.
でもこのように複数の型の値を取りうるデータを処理するときにいちいちXML Schemeを書かなければならないのは骨の折れる仕事です.つまりコストが高いです.もっと簡単にできないか?というのがUnion Typesの提案の趣旨です.
最新のSaxon 10.0ではEEバージョンでないとUnion Typesは使えないのですが、これより以前のバージョンならsaxon:asで表現できます.以下のスタイルシートが入力データにXML Schemeを関連付けなくとも動いてくれるフレキシブルなバージョンです.
xml version="1.0" encoding="UTF-8"
<xslstylesheet xmlnsxsl="http://www.w3.org/1999/XSL/Transform"
xmlnsxs="http://www.w3.org/2001/XMLSchema"
xmlnsmath="http://www.w3.org/2005/xpath-functions/math"
xmlnsahf="http://www.antennahouse.com/names/function"
xmlnssaxon="http://saxon.sf.net/"
exclude-result-prefixes="xs math"
version="3.0">
<xsltemplate match="jeans_size">
<xslvariable name="jeansSize" saxonas="union(xs:positiveInteger,xs:string)" select="."/>
<xslchoose>
<xslwhen test="data($jeansSize) instance of xs:positiveInteger">
<xslmessage select="'Numeric jeans size = ' || ($jeansSize treat as xs:positiveInteger) => string()"/>
</xslwhen>
<xslwhen test="data($jeansSize) instance of xs:string">
<xslmessage select="'Enumerated jeans size = ' || ($jeansSize treat as xs:string)"/>
</xslwhen>
<xslotherwise>
<xslmessage select="'Invalid jeans_size type'"/>
</xslotherwise>
</xslchoose>
</xsltemplate>
</xslstylesheet>
このスタイルシートだと、入力XMLファイルが
xml version="1.0" encoding="UTF-8"
<jeans_size>42</jeans_size>
のようにwell-formedだけのものであっても、ちゃんと
Numeric jeans size = 42
と出てくれます.いちいちスキーマを作る手間がないので実に簡単です.
またUnion Typesは入力データから導かれた変数ばかりに使う必要はありません.次のような例で変数をそれに結び付けて使うこともできます.
xml version="1.0" encoding="UTF-8"
<xslstylesheet xmlnsxsl="http://www.w3.org/1999/XSL/Transform"
xmlnsxs="http://www.w3.org/2001/XMLSchema"
xmlnsmath="http://www.w3.org/2005/xpath-functions/math"
xmlnsahf="http://www.antennahouse.com/names/function"
xmlnssaxon="http://saxon.sf.net/"
exclude-result-prefixes="xs math"
version="3.0">
<xslvariable name="gUnionVar" saxonas="union(xs:date, xs:dateTime, xs:time)" select="current-date()"/>
<xsltemplate match="/">
<xslchoose>
<xslwhen test="$gUnionVar instance of xs:date">
<xslmessage select="'$gUnionVar = ' || format-date($gUnionVar, 'Today is [Y,1,4]-[M,1,2]-[D,1,2]')"/>
</xslwhen>
<xslotherwise>
<xslmessage select="'$gUnionVar is not instance of xs:date'"/>
</xslotherwise>
</xslchoose>
</xsltemplate>
</xslstylesheet>
この場合、ちゃんと
$gUnionVar = Today is 2020-04-07
と出てくれます.
Union TypesはSaxon 10ではEEでないと使えないのがちょっと残念ですが、これはたぶんschme-awareな変換が主な用途、つまり商用での使用が主な使い道と考えてのことなのでしょう.