1
votes

I have a transform.xsl file with will process a input.xml. But there is also an additional config.xml file which will define additional clauses. For e.g. this is the content of the config.xml.

<Location >
  <DisplayName>
     <Attribute1>ABC</Attribute1>
     <Attribute2>XYZ</Attribute2>
     <action>concat($Attribute1,$Attribute2)</action>
  </DisplayName>
</Location >

So when transform.xsl will encounter the DisplayName variable within the input.xml, then it will form the value with the RESULT of the action expression defined in the config.xml file. transform.xml will call the config.xml just to get the result. (The action can be modified by the end user and hence these are placed outside the xsl file, within the config.xml).

We are using saxon xml processor version 9 and xslt 2.0. So we need to use saxon:evaluate(). I tried to find more examples of saxon:evaluate(), but couldn't find it more. Can anyone show me some examples of how to use it?

Thanks in advance.

***** This is an edited query to highlight the need of saxon:evaluate *****

2
I don't see any regular expression in the regex attribute, instead it seems an XPath expression, a call to the concat function with two variable or parameter references as the arguments to concat. It is not clear why you need that instead of just using normal parameters or variables for the stylesheet or in a separate stylesheet module, but in general if you need some dynamic evaluation or generation and execution of code a move to XSLT 3 can help where you have various options like shadow attributes, the transform function plus xsl:evaluate (if supported) to solve such problems. - Martin Honnen
Hi @MartinHonnen: thanks for your response. The use case is that when Location element is searched in the input.xml, it's DisplayName will come from the regex element of the config.xml file. I think we need to go for xsl:evaluate, that's the only way and Saxon supports this. But if I am using <xsl:template match="/"> <xsl:message> VALUE is = <xsl:value-of select="saxon:evaluate($regex)"/> </xsl:message> </xsl:template>, I am getting the following error - Static error in XPath expression supplied to saxon:evaluate: Undeclared variable in XPath expression: $Attribute1 - Nirmalya
Which version of Saxon exactly do you use? You haved tagged your question as xslt-2.0, Saxon since 9.8 supports XSLT 3.0 and there we have the xsl:evaluate element. In earlier versions there is support for an extension function saxon:evaluate. - Martin Honnen
I can't see any circumstances in which you would choose to use an up-to-date version of Saxon and would prefer saxon:evaluate over xsl:evaluate. Although you say you're using XSLT 2.0, it must be better to use a feature standardized in XSLT 3.0 than to use a proprietary Saxon extension, when both are available in your chosen processor. And in any case, xsl;evaluate meets this requirement (by allowing variables with any name), while saxon:evaluate does not (the variables have to be named $p1, $p2, etc). - Michael Kay

2 Answers

1
votes

Here is an example to use an XSLT 3 processor supporting xsl:evaluate (https://www.w3.org/TR/xslt-30/#dynamic-xpath) (i.e. Saxon 9.8 or later with the commercial PE or EE editions or Altova 2017 or later) to process your "config" file:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

    <xsl:param name="config-url" as="xs:string">test2018121301.xml</xsl:param>
    <xsl:param name="config-doc" select="doc($config-url)"/>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:key name="element" match="*" use="node-name()"/>

    <xsl:function name="mf:config-evaluation" as="item()*">
        <xsl:param name="config-doc" as="document-node()"/>
        <xsl:param name="element-name" as="xs:QName"/>
        <xsl:variable name="display" select="key('element', $element-name, $config-doc)/DisplayName"/>
        <xsl:evaluate xpath="$display/regex" with-params="map:merge($display!(* except regex)!map { QName('', local-name()) : string() })"/>
    </xsl:function>

    <xsl:template match="*[key('element', node-name(), $config-doc)]">
        <xsl:copy>
            <xsl:value-of select="mf:config-evaluation($config-doc, node-name()), ."/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

So with a config.xml

<Location >
    <DisplayName>
        <Attribute1>ABC</Attribute1>
        <Attribute2>XYZ</Attribute2>
        <regex>concat($Attribute1,$Attribute2)</regex>
    </DisplayName>
</Location >

this would transform an input sample with e.g.

<Root>
    <Items>
        <Item>
            <Data>data 1</Data>
            <Location>location 1</Location>
        </Item>
        <Item>
            <Data>data 2</Data>
            <Location>location 2</Location>
        </Item>
    </Items>
</Root>

into

<Root>
    <Items>
        <Item>
            <Data>data 1</Data>
            <Location>ABCXYZ location 1</Location>
        </Item>
        <Item>
            <Data>data 2</Data>
            <Location>ABCXYZ location 2</Location>
        </Item>
    </Items>
</Root>

That gives you a great flexibility to allow XPath expressions in the configuration files but as pointed out in https://www.w3.org/TR/xslt-30/#evaluate-effect, also is a security problem: "Stylesheet authors need to be aware of the security risks associated with the use of xsl:evaluate. The instruction should not be used to execute code from an untrusted source.".

As for using the saxon:evaluate function supported in older versions of Saxon not supporting the XSLT 3 xsl:evaluate instruction, a simple example is

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:saxon="http://saxon.sf.net/"
    exclude-result-prefixes="#all"
    version="2.0">

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="example">
        <xsl:copy>
            <xsl:value-of select="saxon:evaluate(@expression, @foo, @bar)"/>
        </xsl:copy>
    </xsl:template>   

</xsl:stylesheet>

which transforms the input

<root>
    <example expression="concat($p1, $p2)" foo="This is " bar="an example."/>
    <example expression="replace(., $p1, $p2)" foo="\p{L}" bar="X">This is example 2.</example>
</root>

into the result

<root>
    <example>This is an example.</example>
    <example>XXXX XX XXXXXXX 2.</example>
</root>
0
votes

Try checking the xsl-attribute tag along with the xsl-value-of tag. If I get what you're asking for, you could probably read the config.xml using the transform.xsl (or a second xsl for an intermediate file) to set the text inside the regex tag to correspond to the value of an tag attribute within the xsl.

https://www.w3schools.com/xml/ref_xsl_el_attribute.asp

Also, check this tutorial for regex in XSLT 2, it may help:

https://www.xml.com/pub/a/2003/06/04/tr.html