programaths
programaths

Reputation: 891

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 ?

Upvotes: 0

Views: 142

Answers (1)

Mathias Müller
Mathias Müller

Reputation: 22617

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.

Upvotes: 1

Related Questions