1

(updated question from 'static replace' to 'dynamic replace')

<data>
  <replacements>
    <replace key="greeting" value='Hello' />
    <replace key="name" value='Donald' />
  </replacements>
  <rules>
    <rule key="GREETING" value="replacements/replace[@key='greeting']/@value" />
    <rule key="NAME" value="replacements/replace[@key='name']/@value" />
  </rules>
  <text>
    GREETING NAME duck.
  </text>
</data>

I'd like to replace the text in the element text with every occurrence of a key in all the rules. The replacement text is the evaluation of the attribute value in that element.

Which approach should I take (using the newest Saxon HE)?

The result of the text should be “Hello Donald duck.”


Addition:

I think part of the problem can be solved with <xsl:evaluate>, but this is obviously not working yet (no string replacements):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" >
  <xsl:output indent="yes"/>

  <xsl:template match="data">
    <xsl:copy>
    <xsl:evaluate xpath="/data/rules/rule[1]/@value" context-item="." />
    </xsl:copy>
  </xsl:template>
   
</xsl:stylesheet>

The result is <data value="Hello"/>. What I see is the dynamic evaluation of the xpath expression, and from there it should be possible to do the string replacement.

Now my step would be to add my approach to Martin's answer, but this would be mixing XSLT in XPath, which does not work that way I want.

topskip
  • 16,207
  • 15
  • 67
  • 99

1 Answers1

2

I don't know what you are trying with xsl:evaluate, consider to show your code if you mention it, but it seems fold-left calling replace suffices:

  <xsl:template match="text" expand-text="yes">
    <xsl:copy>{fold-left(../rules/rule, ., function($a, $r) { replace($a, $r/@key, $r/@value) })}</xsl:copy>
  </xsl:template>
  
  <xsl:template match="data/rules"/>

With the more complicated structure of your "replacement rules and data" you could use

  <xsl:function name="mf:evaluate-value">
    <xsl:param name="rule" as="element(rule)"/>
    <xsl:evaluate context-item="$rule/../.." xpath="$rule/@value"/>
  </xsl:function>

  <xsl:template match="text" expand-text="yes">
      <xsl:copy>{fold-left(../rules/rule, ., function($a, $r) { replace($a, $r/@key, mf:evaluate-value($r)) })}</xsl:copy>
  </xsl:template>
  
  <xsl:template match="data/rules | data/replacements"/>

For the use function you need to declare a namespace in the stylesheet e.g. xmlns:mf="http://example.com/mf".

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110