1
votes

I have two sample documents defined below. In module_meta.xml only the effect nodes on xpath /mdata/effectivity are relevant. As seen below, they contain a path attribute and a effrg attribute. The goal is now to evaluate the xpath (which is defined in the module_meta.xml as the path attribute) on the module.xml and append the effrg to it. See desired_output.xml for the desired result. The xsl transformation is applied on module.xml. I know that I have to use the document() function to "include" module_meta.xml, but so far I am at a loss.

module.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE proc>
    
<procbody>
 <info>
  <action lid="a">
  </action>
  <action lid="b">
  </action>
  <action lid="c">
  </action>
 </info>
</procbody>

module_meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mdata>
<mdata>
  <metadata>
    <metadata-item name="n1" value="v1" />
    <metadata-item name="n2" value="v2" />
    <metadata-item name="n3" value="v3" />
  </metadata>
  <effectivity>
    <effect path="//*[@lid='a']" effrg="0074 0080 0087" />
    <effect path="//*[@lid='b']" effrg="0136 0146 0174" />
  </effectivity>
</mdata>

desired_output.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE proc>
        
<procbody>
 <info>
  <action lid="a" effrg="0074 0080 0087">
  </action>
  <action lid="b" effrg="0136 0146 0174">
  </action>
  <action lid="c">
  </action>
 </info>
</procbody>
1
XSLT 3 (as supported by Saxon 10 and later all editions, Saxon 9.8 and later PE and EE, by Saxon-JS 2 and by Altova XML 2017 R3 and later) has xsl:evaluate. Some older implementations provide extension functions or let you set one up for XPath evaluation. Or you can chain two stylesheets.Martin Honnen

1 Answers

1
votes

In XSLT 3 with xsl:evaluate support:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  xmlns:mf="http://example.com/mf"
  expand-text="yes">
  
  <xsl:function name="mf:evaluate" as="element(action)?">
    <xsl:param name="effect" as="element(effect)"/>
    <xsl:evaluate xpath="$effect/@path" context-item="$main-doc"/>
  </xsl:function>

  <xsl:variable name="main-doc" select="/"/>
  
  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:template match="info/action">
    <xsl:copy>
      <xsl:apply-templates select="@*, $meta//effect[current() is mf:evaluate(.)]/@effrg"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:param name="meta">
<mdata>
  <metadata>
    <metadata-item name="n1" value="v1" />
    <metadata-item name="n2" value="v2" />
    <metadata-item name="n3" value="v3" />
  </metadata>
  <effectivity>
    <effect path="//*[@lid='a']" effrg="0074 0080 0087" />
    <effect path="//*[@lid='b']" effrg="0136 0146 0174" />
  </effectivity>
</mdata>
  </xsl:param>
  
</xsl:stylesheet>

For self-containedness of the example the second document is inline as a parameter but you can of course use <xsl:param name="meta" select="doc('module_meta.xml')"/> instead.

It might be much more efficient to use the xsl:evaluate only once for each effect element, for instance, by declaring a key doing that, and store the generated-id if there is one:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  xmlns:mf="http://example.com/mf"
  expand-text="yes">
  
  <xsl:key name="ref" match="mdata/effectivity/effect">
    <xsl:variable name="referenced-node" as="node()?">
      <xsl:evaluate xpath="@path" context-item="$main-doc" as="node()?"/>
    </xsl:variable>
    <xsl:sequence select="generate-id($referenced-node)"/>
  </xsl:key>

  <xsl:variable name="main-doc" select="/"/>
  
  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:template match="info/action">
    <xsl:copy>
      <xsl:apply-templates select="@*, key('ref', generate-id(), $meta)/@effrg"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:param name="meta">
<mdata>
  <metadata>
    <metadata-item name="n1" value="v1" />
    <metadata-item name="n2" value="v2" />
    <metadata-item name="n3" value="v3" />
  </metadata>
  <effectivity>
    <effect path="//*[@lid='a']" effrg="0074 0080 0087" />
    <effect path="//*[@lid='b']" effrg="0136 0146 0174" />
  </effectivity>
</mdata>
  </xsl:param>
  
</xsl:stylesheet>

If you want to match any element referenced in the other document and copy any attribute but the path one you could change the template having match="info/action" to

  <xsl:template match="*[key('ref', generate-id(), $meta)]">
    <xsl:copy>
      <xsl:apply-templates select="@*, key('ref', generate-id(), $meta)/(@* except @path), node()"/>
    </xsl:copy>
  </xsl:template>