0
votes

I want to copy the every node expect the first node and change value of child node based on value in source XML.

I am able to copy node and and change value of tag based on source value. But I am not able to copy rest of nodes like App, header, data, sender...

Source XML:

<root>
    <App>
        <header>
            <sender>
                <name>PC1</name>
            </sender>
        </header>
        <data>
            <A></A>
            <B></B>
            <C></C>
        </data>
    </App>
</root>

XSLT:

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

    <xsl:template match="/root/App/header/sender">
        <xsl:choose>
            <xsl:when test="name">
                <xsl:apply-templates select="name" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:element name="name">
                    <xsl:value-of select="'Desktop'" />
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="/root/App/header/sender/name">
    <xsl:element name="name">
        <xsl:choose>
            <xsl:when test="not(string(.))">
                <xsl:value-of select="'Desktop'" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat('Desktop-',.)" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:element>
</xsl:template>

Current output:

<root>
   <name>Desktop-PC1</name>
</root>

Expected output:

<App>
    <header>
        <sender>
            <!-- if tag <name> has some value -> add prefix "Desktop-" -->
            <name>Desktop-PC1</name>

            <!-- if tag <name> has empty value or whole tag missing  -> add default value "Desktop" 
            <name>Desktop</name>
            --> 
        </sender>
    </header>
    <data>
        <A></A>
        <B></B>
        <C></C>
    </data>
</App>
1

1 Answers

2
votes

For such tasks I would always start with the identity transformation template and then match on that particular element (as long as it is available) or on the parent if the child is not available:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:param name="default" select="'Desktop'"/>

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

  <xsl:template match="App/header/sender/name[normalize-space()]">
      <xsl:copy>
          <xsl:value-of select="$default"/>-<xsl:value-of select="."/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="App/header/sender/name[not(normalize-space())]">
      <xsl:copy>
          <xsl:value-of select="$default"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="App/header/sender[not(name)]">
      <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <name>
            <xsl:value-of select="$default"/>
          </name>
          <xsl:apply-templates/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bwdwrB/0, https://xsltfiddle.liberty-development.net/bwdwrB/1, https://xsltfiddle.liberty-development.net/bwdwrB/2

The other transformation task to solely output the App child of the root element is then solved y adding a template for that root that simply uses apply-templates

  <xsl:template match="root">
      <xsl:apply-templates/>
  </xsl:template>

https://xsltfiddle.liberty-development.net/bwdwrB/3