xmlxsltsaxonschematron

custom namespace "localFunction" in Schematron file causes transformation crash


I have a Schematron file (.sch) which contains a namespace like "localFunctions". It is used for calling a local function in same file. But when I try to make a validation, compiling Schematron file with schxslt (10.1.0) then transforming xml and Schematron with SaxonCS (12.5.0) and it causes an error code with "SXST0001". When I go deep down to error location and comment out the function calling line, everything works perfect. I have tried to give a namespace like "http://example.com/fct" but nothing changed.

Here is the Schematron file's namespace

<schema xmlns="http://purl.oclc.org/dsdl/schematron" xmlns:sch="http://purl.oclc.org/dsdl/schematron" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fct="localFunctions" queryBinding="xslt2">
    <ns prefix="gl-plt" uri="http://www.xbrl.org/int/gl/plt/2010-04-16"/>
    <ns prefix="gl-cor" uri="http://www.xbrl.org/int/gl/cor/2006-10-25"/>
    <ns prefix="gl-bus" uri="http://www.xbrl.org/int/gl/bus/2006-10-25"/>
    <ns prefix="gl-muc" uri="http://www.xbrl.org/int/gl/muc/2006-10-25"/>
    <ns prefix="xbrli" uri="http://www.xbrl.org/2003/instance"/>
    <ns prefix="ds" uri="http://www.w3.org/2000/09/xmldsig#"/>
    <ns prefix="xades" uri="http://uri.etsi.org/01903/v1.3.2#"/>
    <ns prefix="edefter" uri="http://www.edefter.gov.tr"/>
    <ns prefix="fct" uri="localFunctions"/>
    <ns prefix="defterek" uri="http://www.edefter.gov.tr/ek"/>
    <title>Büyük defter dokümanlarını kontrol etmek için gerekli olan schematron kuralları</title>
    <let name="periodCoveredStart" value="/edefter:defter/xbrli:xbrl/gl-cor:accountingEntries/gl-cor:documentInfo/gl-cor:periodCoveredStart"/>
    <let name="periodCoveredEnd" value="/edefter:defter/xbrli:xbrl/gl-cor:accountingEntries/gl-cor:documentInfo/gl-cor:periodCoveredEnd"/>
    <let name="vknTckn" value="/edefter:defter/xbrli:xbrl/xbrli:context/xbrli:entity/xbrli:identifier"/>
    <let name="donemYil" value="substring($periodCoveredStart,1,4)"/>
    <let name="donemAy" value="substring($periodCoveredStart,6,2)"/>
    <let name="donem" value="number(concat($donemYil,$donemAy))"/>
    <let name="dosyaAdi" value="base-uri()"/>

Here is the rule which causes an issue

<pattern id="accountingentries">
        <rule context="/edefter:defter/xbrli:xbrl/gl-cor:accountingEntries">
            <assert test="gl-cor:documentInfo">gl-cor:documentInfo zorunlu bir elemandır.</assert>
            <assert test="gl-cor:entityInformation">gl-cor:entityInformation zorunlu bir elemandır.</assert>
            <let name="accoundMainIdList" value="gl-cor:entryHeader/gl-cor:entryDetail[1]/gl-cor:account/normalize-space(gl-cor:accountMainID)"/>
            ---Commenting this line fixes everything. But this function is also part of the validation. ---><assert test="fct:isSorted($accoundMainIdList)">Büyük defterde hesaplar, ana hesap numarası bazında sıralı olmalıdır.</assert>
            <let name="altHesabiOlmayanAnaHesapListesi" value="gl-cor:entryHeader/gl-cor:entryDetail[1]/gl-cor:account[count(gl-cor:accountSub)=0]/normalize-space(gl-cor:accountMainID)"/>
            <let name="altHesabiOlmayanAnaHesapSayisi" value="count($altHesabiOlmayanAnaHesapListesi)"/>
            <let name="farkliAltHesabiOlmayanAnaHesapSayisi" value="count(distinct-values($altHesabiOlmayanAnaHesapListesi))"/>
            <assert test="$altHesabiOlmayanAnaHesapSayisi = $farkliAltHesabiOlmayanAnaHesapSayisi">Alt hesabı olmayan aynı hesaplar aynı gl-cor:entryHeader elemanı içerisinde bulunmalıdır.</assert>
            <let name="altHesapListesi" value="gl-cor:entryHeader/gl-cor:entryDetail[1]/gl-cor:account/gl-cor:accountSub/normalize-space(gl-cor:accountSubID)"/>
            <let name="altHesapSayisi" value="count($altHesapListesi)"/>
            <let name="farkliAltHesapSayisi" value="count(distinct-values($altHesapListesi))"/>
            <assert test="$altHesapSayisi = $farkliAltHesapSayisi">Aynı alt hesaplar aynı gl-cor:entryHeader elemanı içerisinde bulunmalıdır.</assert>
        </rule>
    </pattern>

and here is the function definition

<xsl:function name="fct:isSorted" as="xs:boolean">
        <xsl:param name="accoundMainIdList" as="xs:string*"/>
        <xsl:variable name="sortedAccountMainIdList" as="xs:string*">
            <xsl:for-each select="$accoundMainIdList">
                <xsl:sort/>
                <xsl:value-of select="."/>
            </xsl:for-each>
        </xsl:variable>
        <xsl:variable name="s1">
            <xsl:value-of select="string-join($accoundMainIdList,'|')"/>
        </xsl:variable>
        <xsl:variable name="s2">
            <xsl:value-of select="string-join($sortedAccountMainIdList,'|')"/>
        </xsl:variable>
        <xsl:choose>
            <xsl:when test="$s1 = $s2">
                <xsl:value-of select="true()"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="false()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>

Solution

  • The rule in the Schematron ISO spec of 2016 says "The XSLT2 function element may be used, in the XSLT2 namespace, before the pattern elements.".

    I think Schxslt implements that precisely as required by the spec while perhaps other Schematron implementations are or in the past were more lenient so that Schematron authors who test against those implementations might publish a schema that doesn't work as intended with Schxslt.