1

I am trying to find a way to add an element to input xml file via XSLT only if it doesn't already exist. My solution below works in cases where the element doesn't exist, however if it DOES exist (I still need to put the value for sessionId), it still creates a new one.

XSL:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="xsl exsl xs">
    <xsl:output method="xml" version="1.0" indent="yes" encoding="utf-8" />
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="//*[local-name() = 'SessionHeader']">
        <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
            <xsl:choose>
                <xsl:when test="not(sessionId)">
                    <sessionId><xsl:value-of select="9876543210"/></sessionId>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy><xsl:value-of select="9876543210"/></xsl:copy>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Sample XML:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <SessionHeader>
      <sessionId />
    </SessionHeader>
  </soap:Header>
  <soap:Body>
      ....
  </soap:Body>
</soap:Envelope>

After XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soap:Header>
      <SessionHeader>
         <sessionId />
         <sessionId xmlns="">9876543210</sessionId>
      </SessionHeader>
   </soap:Header>
   <soap:Body>
       ....
   </soap:Body>
</soap:Envelope>

Note the 2 sessionId elements

Again, if the sessionId element doesn't exist at all, it works fine. Thanks in advance for any useful help.

1
  • 1
    Your request is (still) not clear. It seems like you need to handle three possible scenarios: (a) sessionId does not exist at all; (b) it exists , but is empty; and (c) it exists and contains a value. Please explain how you want to deal with each one of these. -- Also, is there a good reason why you are ignoring - or rather mishandling - the namespaces? Commented Feb 2, 2015 at 19:28

2 Answers 2

1

I think (or rather guess) you want to do something like:

XSLT 1.0

<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="SessionHeader[not(sessionId)]">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
        <sessionId><xsl:value-of select="9876543210"/></sessionId>
    </xsl:copy>
</xsl:template>

<xsl:template match="sessionId[not(text())]">
    <xsl:copy>
        <xsl:text>9876543210</xsl:text>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

This adds the value "9876543210" to the sessionId element if it's empty or creates a new sessionId element with the above value if it doesn't exist. Otherwise the default identity transforn template will copy the existing sessionId element, along with its existing value.

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

3 Comments

FYI, only had to modify above solution a tad for specific condition: <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="//*[local-name() = 'SessionHeader'][not(*[local-name() = 'sessionId'])]"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> <sessionId><xsl:value-of select="9876543210"/></sessionId> </xsl:copy> </xsl:template> <xsl:template match="*[local-name() = 'sessionId'][not(text())]"> <xsl:copy><xsl:text>9876543210</xsl:text></xsl:copy> </xsl:template>
FYI, you should not use *[local-name()='xyz'] if you know the fully-qualified name of the node in question. And you certainly do not need // at the beginning of a match pattern.
I concur re: the "local-name()". However, this snippet is just a part of a whole bunch of others and management doesn't see a need to modify the other XSLs too to be consistent.
0

If you can safely assume that sessionId will always be the only child of SessionHeader, then here is an easy way to accomplish 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 template -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="SessionHeader[not(normalize-space(sessionId))]">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <sessionId>9876543210</sessionId>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

If SessionHeader has no sessionId or an empty/all whitespace sessionId, it simply creates one with the predefined value. If it does already contain a sessionId with a value, then the identity template takes care of copying it.

2 Comments

However, in the case where sessionId exists but without a value, this will not work. @michael.hor257k solution works perfectly with slight modifications for some unique conditions. I added the final solution under another comment.
@Pratik This will work when sessionId exists without a value. Did you try it, or are you just assuming? xpathtester.com/xslt/e0e07dfcd771a9acbffc210fc5769659

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.