0

I am trying to transform attributes to node and replace values to correct one. transforming and join works well but I can't figure out how to replace tranformed data. I tried choose and loop through attributes without any result.

Here is source XML

<cats>
  <cat id="11">Foo 1</cat>
  <cat id="12">Foo 2</cat>
</cats>

My XLS - this part works

<xsl:template match="cats">
    <cat-id>
       <xsl:value-of select="string-join(cat/@id, ',')" />
     </cat-id>
</xsl:template>

Replacement table 11 => 24, 12 => 75, 13 => 145 ...

Result I want to achieve

<cat-id>24,75</cat-id>
4
  • You have tagged this as XSLT1.0 but string-join() requires XSLT 2.0. If the above works for you, then you must be using an XSLT 2.0 processor. -- Also, where do you want to keep the replacement table? Can it be hard-coded in the XSLT stylesheet? Commented Nov 29, 2018 at 14:02
  • Sorry my mistake.. I testing it in with Saxon 9.8 and I did't realize that I have xsltproc on server (I didn't run on server yet). Thank you for noticing it. Commented Nov 29, 2018 at 14:58
  • So are you still looking for an XSLT 1.0 solution? Commented Nov 29, 2018 at 15:13
  • Yes, it will be best for me, otherwise I have to install some new xls processor on server. Commented Nov 29, 2018 at 16:17

2 Answers 2

1

You can write a function that maps each id attribute value to its replacement, in XSLT 3 (supported since 2017 by Saxon 9.8 and later and Altova 2017 and later) you can use a map as a function:

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

    <xsl:param name="replacement-map" as="map(xs:integer, xs:integer)" select="map { 11 : 24, 12 : 75, 13 : 145 }"/>

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

    <xsl:template match="cats">
        <cat-id>
           <xsl:value-of select="cat/@id/$replacement-map(xs:integer(.))" separator="," />
         </cat-id>
    </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/jyH9rNs

In XSLT 2 you could use an XML structure to represent your replacement table/map and use a key to find the replacement for an attribute value:

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

    <xsl:param name="replacement-map">
        <value key="11">24</value>
        <value key="12">75</value>
        <value key="13">145</value>
    </xsl:param>

    <xsl:key name="rep" match="value" use="@key"/>

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

    <xsl:template match="cats">
        <cat-id>
           <xsl:value-of select="cat/@id/key('rep', ., $replacement-map)" separator="," />
         </cat-id>
    </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/jyH9rNs/1

Finally in XSLT 1, as the key function doesn't have a third parameter to change the context doc, you could use

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    xmlns:msxml="urn:schemas-microsoft-com:xslt"
    exclude-result-prefixes="exsl msxml"
    version="1.0">

  <xsl:param name="replacement-map-rtf">
    <value key="11">24</value>
    <value key="12">75</value>
    <value key="13">145</value>
  </xsl:param>

  <xsl:param name="replacement-map" select="exsl:node-set($replacement-map-rtf)"/>

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

  <xsl:key name="rep" match="value" use="@key"/>

  <xsl:template match="cats">
      <cat-id>
          <xsl:apply-templates select="cat/@id"/>
      </cat-id>
  </xsl:template>

  <xsl:template match="cat/@id">
      <xsl:if test="position() > 1">,</xsl:if>
      <xsl:variable name="this" select="."/>
      <xsl:for-each select="$replacement-map">
          <xsl:value-of select="key('rep', $this)"/>
      </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/jyH9rNs/2

Sign up to request clarification or add additional context in comments.

Comments

0

Here is how you can do it in XSLT 1.0:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://www.example.com/my"
exclude-result-prefixes="my">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<my:replacement-map>
    <value key="11">24</value>
    <value key="12">75</value>
    <value key="13">145</value>
</my:replacement-map>

<xsl:template match="cats">
    <cat-id>
        <xsl:for-each select="cat">
            <xsl:value-of select="document('')/xsl:stylesheet/my:replacement-map/value[@key=current()/@id]" />
            <xsl:if test="position() != last()">
                <xsl:text>,</xsl:text>
            </xsl:if>
        </xsl:for-each>
    </cat-id>
</xsl:template>

</xsl:stylesheet>

If you have many values, this could be made a bit more efficient by using a key. But pointing a key to a different document (the XSLT stylesheet itself, in this case) is awkward in XSLT 1.0.

2 Comments

Thank you, but I tried your solution in fiddle and I got only <cat-id>,</cat-id>
@Destrosvet It will not work in an online test tool, because of the reference to the XSLT document - which doesn't really exist.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.