xsltxpathmap-force

Mapforce generates oddities


I took over a project with files generated with Mapforce. I read something like :

*[local-name()='no_regnat' and namespace-uri()=''][not((not((translate(string(@xsi:nil), 'true ', '1') = '1')) and (string(.) = '')))]

It seems it can be writen like this

no_regnat[not((boolean(@xsi:nil) and (string(.) = '')))]

Why is it doing the former ?


Solution

  • Let us first consider the left side of the expressions,

    *[local-name()='no_regnat' and namespace-uri()='']
    

    and

    no_regnat
    

    In most contexts, they mean exactly the same, but in very specific situations, they do not yield the same result sequence:

    If you define an xpath-default-namespace in your XSLT 2.0 stylesheet, the expressions do not deliver the same results:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xpath-default-namespace="www.no_regnat.com">
    
        <xsl:output method="xml" indent="yes"/>
    
        <xsl:template match="/">
            <results>
                <xsl:apply-templates/>
            </results>
        </xsl:template>
    
        <xsl:template match="*[local-name()='no_regnat' and namespace-uri()='']">
           <!--Or: template match="no_regnat"-->
            <xsl:copy-of select="."/>
        </xsl:template>
    
    </xsl:stylesheet>
    

    Test the above with, for instance,

    <?xml version="1.0" encoding="UTF-8"?>
    <root xmlns:r="www.no_regnat.com">
        <no_regnat n="1"/>
        <r:no_regnat n="2"/>
        <other xmlns="www.no_regnat.com">
            <no_regnat n="3"/>
        </other>
    </root>
    

    And directly transform it online here.

    So, we would need to see the context of those expressions to decide whether Mapforce is indeed generating code that is too verbose.


    Then, the second predicate:

    translate(string(@nil), 'true ', '1')
    

    is really odd, in my opinion. The translate() function only ever works with single characters, and that's why the strings that are the second and third argument of translate() usually have the same length. Characters in the second argument that do not have a counterpart in the third argument are translated to the empty string.

    So, what the function is doing here is mapping t to 1, and mapping r, u and e and (whitespace) to nothing. boolean() is much more useful here.


    But be careful with the semantics of those predicates:

    [not((not((translate(string(@xsi:nil), 'true ', '1') = '1')) and (string(.) = '')))]
    

    means

    do not allow cases where xsi:nil is false and the string value of the context element is empty

    Whereas

    [not((boolean(@xsi:nil) and (string(.) = '')))]
    

    means

    do not allow cases where xsi:nil is true and the string value of the context element is empty

    Most likely, the correct constraint is: if xsi:nil = 'true', then the context element must be empty.