XSLT3.0への道(34) 便利なtuple

関数やテンプレートから複数の値を呼び出し元に返したいことはなかったでしょうか?

普通はめったにお目にかからないのですが、超高機能(というか複雑)な処理を関数やテンプレートで行う場合、「あ~!複数の結果を返せたらな!」と考えることがしばしばあります.

XSLT 1.0ではこのような機能は皆無でした.そもそもXSLT 1.0には関数もありませんでしたね.もし複数の結果を返そうとしたら、テンポラリツリーを作って、そこに結果を返すような芸当しかできなかったと思います.

XSLT 2.0ではシーケンスという概念が明確に導入されたので、as="item()*"なんて書けば、複数の結果を返すことができるようになりました.これにどれほどお世話になったかわかりませんが、欠点は型に弱いことです.item()xs:integerxs:stringも表現できますが、node()も表現できます.だから結果の1番目はどんな型で、2番目はどんな型というような約束事を定めねばなりません.

あとこの表現方法の致命的な問題は、ノードを返す時です.item()*item()+で1個づつのnode()を返すときはいいんですが、node()+をいくつか返したい場合はどうなるでしょう?

実はこれはできないのです.それはnode()+item()[1]とすることはできない、つまりシーケンスのシーケンスというのはXSLT 2.0ではそもそも存在していなかったからです.ですからnode()+を3個返そうとするとそれらは全部1つのシーケンスに統合されてしまいます.当たり前と言えば当たり前なのですが.

ではXSLT 3.0ではどうでしょうか?一つの有力な手としては配列があります.配列は個々のメンバーがnode()+であってもOKのはずだからです.しかし配列というと、サイズが問題になります.呼び出し側との間で決まりを作っておけば良いのですが、まだいまいちパッとしません.

XSLT 3.0の仕様にはないのですが、Saxon 9.8以上ならtuple(タプル)が使えます.タプルは、

  • 基本的に決まった個数のメンバーを
  • 静的な型付けで定義し、格納すること

ができます.ですのでせいぜい3個位までの戻り値を作りたくて、1番目はxs:integer、2番目はxs:string、3番目はattribute()なんていうふうに予め宣言できるのです.これなら絶対間違いはありません.以下は極めて簡単な例ですが、関数の戻り値を間違いなく受け取ることができることを示しています.

<!--?xml version="1.0" encoding="UTF-8"?-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
 xmlns:xs="http://www.w3.org/2001/XMLSchema" 
 xmlns:math="http://www.w3.org/2005/xpath-functions/math" 
 xmlns:saxon="http://saxon.sf.net/" 
 xmlns:map="http://www.w3.org/2005/xpath-functions/map" 
 xmlns:ahf="http://www.antennahouse.com/names/XSLT/Functions/Document" 
 exclude-result-prefixes="xs math saxon" version="3.0">
    
    <xsl:function name="ahf:tupleTest" saxon:as="tuple(int: xs:integer, str: xs:string, att: attribute())">
        <xsl:variable name="attEngine" as="attribute()">
            <xsl:attribute name="engine" select="'YAMAHA'">
        </xsl:variable>
        <xsl:sequence select="map{'int':999, 'str':'TYS-125F', 'att':$attEngine}">
    </xsl:function>
    
    <xsl:template match="/">
        <xsl:variable name="result" saxon:as="tuple(int: xs:integer, str: xs:string, att: attribute())" select="ahf:tupleTest()">
        <xsl:message select="'result:int=',$result?int">
        <xsl:message select="'result:str=',$result?str">
        <xsl:message select="'result:att=',$result?att">
    </xsl:template>
</xsl:stylesheet>

結果は次のように出ます.

result:int= 999
result:str= TYS-125F
result:att=<?attribute name="engine" value="YAMAHA"?>

このtupleの解説は以下のSaxonicaのドキュメントにあります.

https://www.saxonica.com/html/documentation/extensions/syntax-extensions/tuple-types.html

まだWebにはあまり使用例とか載っていませんが、一度お試しください.なおドキュメントにもあるように、Saxon PE/EEが必要です.XMLエディタのoXygenをお持ちでしたら、oXygenのIDEの中から試すことができます.