10

I am working on XSLT, where I need to implement something as follows. My Source XML sample looks like this.

<?xml version="1.0" encoding="ISO-8859-1"?>
    <catalog>
        <cd>
            <title>A</title>  
            <title>B</title>
            <title>C</title>  
        </cd>
    </catalog>

Consider there is some key value pair list is there.

    Key         Value
    A           Algebra
    B           Biology
    C           Chemistry
    D           Data Analysis
    ---         ---

    ----        ---

I need to write an xslt such that for every occurance of key 'A', need to replace with appropriate value.

I also need to mention the list of Key value pairs in the same XSLT. Sample Output:

<Data>
    <Subject>Algebra</Subject>
    <Subject>Biology</Subject>
    <Subject>Chemistry</Subject>
 </Data>

Can any one help me out how to do it.

Thank you.

Patan
  • 17,073
  • 36
  • 124
  • 198

1 Answers1

16

I. Simple XSLT 1.0 Solution

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:codes>
   <code key="A" value="Algebra"/>
   <code key="B" value="Biology"/>
   <code key="C" value="Chemistry"/>
   <code key="D" value="Data Analysis"/>
 </my:codes>

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

 <xsl:template match=
  "title/text()[. = document('')/*/my:codes/*/@key]">

  <xsl:value-of select=
   "document('')/*/my:codes/*[@key=current()]/@value"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<catalog>
    <cd>
        <title>A</title>
        <title>B</title>
        <title>C</title>
    </cd>
</catalog>

produces the wanted, correct result:

<catalog>
   <cd>
      <title>Algebra</title>
      <title>Biology</title>
      <title>Chemistry</title>
   </cd>
</catalog>

Explanation:

This is the standard way of including inline XML node as a global element (child element of xsl:stylesheet) that belongs to a (non-empty) namespace, different than the xsl namespace.


II. More efficient XSLT 1.0 solution, using keys:

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:my="my:my">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

     <my:codes>
       <code key="A" value="Algebra"/>
       <code key="B" value="Biology"/>
       <code key="C" value="Chemistry"/>
       <code key="D" value="Data Analysis"/>
     </my:codes>

     <xsl:key name="kCodeByName" match="code" use="@key"/>

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

     <xsl:template match=
      "title/text()[. = document('')/*/my:codes/*/@key]">

      <xsl:variable name="vCur" select="."/>

      <xsl:for-each select="document('')">
          <xsl:value-of select=
           "key('kCodeByName', $vCur)/@value"/>
      </xsl:for-each>
     </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the same XML document (above), the same correct, wanted result is produced:

<catalog>
   <cd>
      <title value="Algebra"/>
      <title value="Biology"/>
      <title value="Chemistry"/>
   </cd>
</catalog>

Explanation:

Accessing data via the key() function is typically sub-linear -- often O(1) and is extremely faster than linear search (which is important if the number of nodes to be searched is big).

Accessing a node of one document via an index (xsl:key) while processing a node of another document is possible if the document containing the node to be looked-up is the current document. To access nodes from the other document, its root (or node of interest need to be saved and referenced off a variable.)


III. XSLT 2.0 solution:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vCodes">
  <codes>
   <code key="A" value="Algebra"/>
   <code key="B" value="Biology"/>
   <code key="C" value="Chemistry"/>
   <code key="D" value="Data Analysis"/>
  </codes>
 </xsl:variable>

 <xsl:key name="kCodeByName" match="code" use="string(@key)"/>

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

 <xsl:template match=
  "title/text()[key('kCodeByName', ., $vCodes)]">

  <xsl:sequence select=
   "key('kCodeByName', ., $vCodes)/@value"/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the same XML document (above), the same correct, wanted result is produced:

<catalog>
   <cd>
      <title value="Algebra"/>
      <title value="Biology"/>
      <title value="Chemistry"/>
   </cd>
</catalog>

Explanation:

Almost the same as the efficient XSLT 1.0 solution, but:

  1. In XSLT 2.0 a template match pattern can contain a variable reference.

  2. In XSLT 2.0 there is no need for acrobatic tricks manipulating the current and the indexed documents -- the 3rd argument of the key() function is to specify the tree whose index to use.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • @Dimitre Can we call the template in solution III. XSLT 2.0 in another template. If yes could you please let me know how? – Jyotish Singh Apr 05 '16 at 11:36
  • How can I create key value array with dynamic key and value within for each loop? – Kishan Patadia Jan 23 '21 at 07:36
  • @KishanPatadia, You need to be more specific in order for someone to understand exactly what you are asking. – Dimitre Novatchev Jan 23 '21 at 17:42
  • @DimitreNovatchev I want to create key value pair by iterating category children nodes. It has id and name attribute. I want to store that id(key) and name(value) as pairs(array) using XSLT. – Kishan Patadia Jan 23 '21 at 18:24
  • @KishanPatadia, Please ask a new question, provide as much data as possible, about the input, the desired output and the wanted rules/restrictions of the transformation. Then many people will be able to help. – Dimitre Novatchev Jan 23 '21 at 19:12