'Creating new attribute based on another attribute value in XML using XSLT

I just started learning xslt. I need this input xml file :

<response>
<type>Some type</type>
<attribute name="First"/>
<attribute name="Second"/>
...
<attribute name="nth"/>
  <attribute name="start_action">
    <ActionValue>
      <entryMap name="entryvalue1">
        <action>
          <type>some app statement</type>
          <attribute name="type" value="apps jump" />
        </action>
      </entryMap>
      <entryMap name="entryvalue2">
        <action>
          <type>some phase statement</type>
          <attribute name="type" value="phases2" />
        </action>
      </entryMap>
      <entryMap name="entryvalue3">
        <action>
          <type>some phase statement</type>
          <attribute name="type" value="phases3" />
        </action>
      </entryMap>
      <entryMap name="...">
        <action>
          <type>...</type>
          <attribute name="type" value="..." />
        </action>
      </entryMap>
    </ActionValue>
  </attribute>
</response>

(The ... are for generalizing the input xml so that more phases and apps sections can be added.)

to be transformed to this output xml file :

<response>
<type>Some type</type>
<attribute name="First"/>
<attribute name="Second"/>
...
<attribute name="nth"/>
  <attribute name="start_action">
    <ActionValue>
      <entryMap name="entryvalue1">
        <action>
          <type>some app statement</type>
          <attribute name="type" value="apps jump" />
          <attribute name="app_name" value="entryvalue1" />
        </action>
      </entryMap>
      <entryMap name="entryvalue2">
        <action>
          <type>some phase statement</type>
          <attribute name="type" value="phases2" />
          <attribute name="phase_name" value="entryvalue2" />
        </action>
      </entryMap>
      <entryMap name="entryvalue3">
        <action>
          <type>some phase statement</type>
          <attribute name="type" value="phases3" />
          <attribute name="phase_name" value="entryvalue3" />
        </action>
      </entryMap>
      <entryMap name="...">
        <action>
          <type>...</type>
          <attribute name="type" value="..." />
          <attribute name="..." value="..." />
        </action>
      </entryMap>
    </ActionValue>
  </attribute>
</response>

So, in the input file when I have <attribute name="type" value="apps jump" /> I need to add <attribute name="app_name" value="entryvalue1" /> right after <attribute name="type" value="apps jump" />. And when I have <attribute name="type" value="phases2" /> or <attribute name="type" value="phases3" /> or ANY VALUE in value="" in <attribute name="type"> other than "apps jump" then I need to add <attribute name="phase_name" value="entryvalue2" /> right after the attribute with type element i.e. for the 2nd case <attribute name="type" value="phases2" />. Also, the value in <attribute name="app_name" value="entryvalue1" /> and <attribute name="phase_name" value="entryvalue2" /> and so on, holds the value of their corresponding entryMap name value.

Here for this example I have 1 app and 2 phase sections with app defined first and then the 2 phases are defined but that is not fixed. I can have multiple app and phase sections in any position in my input xml but on the same level. For eg. I can have 2 apps and then 3 phases and then 6 apps and then 1 phase. So I'm trying to create a generalized xslt which will work on any number of app and phase sections.

The current sample XSLT I have right now is this :

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

  <!-- Identity transform -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="action">
    <xsl:copy>
        <xsl:copy-of select="*"/>
        <attribute name="phase_name" value="{../@name}"/>
        <attribute name="app_name" value="{../@name}"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="action">
        <xsl:for-each select="attribute">
            <xsl:if test="@name='phase_name'">
            <xsl:value-of select="@value"/>
            </xsl:if>
            <xsl:if test="@name='app_name'">
            <xsl:value-of select="@value"/>
            </xsl:if>
        </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

I am not able to incorporate my logic into the XSLT. If someone could please help me out with this, I'd be very grateful. Thank you in advance.



Solution 1:[1]

You've written the problem description as a set of condition-action rules, which is great: what you need to do is translate these directly into template rules. For example:

So, in the input file when I have <attribute name="type" value="apps jump" /> I need to add <attribute name="app_name" value="entryvalue1" /> right after <attribute name="type" value="apps jump" />.

That becomes

<xsl:template match="attribute[@name='type'][@value='apps jump']">
  <xsl:copy/>
  <attribute name="app_name" value="{ancestor::entryMap/@name}"/>
</xsl:template

And when I have <attribute name="type" value="phases2" /> or <attribute name="type" value="phases3" /> or ANY VALUE in value="" in <attribute name="type"> other than "apps jump" then I need to add <attribute name="phase_name" value="entryvalue2" /> right after the attribute with type element i.e. for the 2nd case <attribute name="type" value="phases2" />.

This one becomes something like

<xsl:template match="attribute[@name='type'][@value!='apps jump']">
  <xsl:copy/>
  <attribute name="app_name" value="{ancestor::entryMap/@name}"/>
</xsl:template>

But I obviously haven't quite understood your logic because these rules are identical. I'm not sure whether entryMap1 etc in your rules are literal values or values taken from the source document.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Michael Kay