1

Dear Experts, I have to use multiple for-loops in XSLT.

Presently with my XSLT I generate output with extra nodes at 'GroupDetail'.

Input Request

  <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
    xmlns:v1="http://xmldefs. ag.com/Applications/eer/V1" xmlns:wsa="http://www.w3.org/2005/08/addressing">
    <SOAP-ENV:Header>
    <wsa:messageId>04383-34380-3439939</wsa:messageId>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
    <v1:ProcessDistr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <v1:Group xmlns:ns2="http://xmldefs.ag.com/DD/Commons">
        <v1:GroupID>437848</v1:GroupID>
        <v1:GroupDetails>
         <v1:GroupDetail>
                <v1:language>De</v1:language>
                <v1:description>Deutsch</v1:description>
         </v1:GroupDetail>
         <v1:GroupDetail>
                <v1:language>En</v1:language>
                <v1:description>English</v1:description>
         </v1:GroupDetail>
        </v1:GroupDetails> 
        <v1:Status>true</v1:Status>
        <v1:Parent>45434554</v1:Parent>
      </v1:Group>
      <v1:Group xmlns:ns2="http://xmldefs.ag.com/DD/Commons">
        <v1:GroupID>437849</v1:GroupID>
        <v1:GroupDetails>
         <v1:GroupDetail>
                <v1:language>Tu</v1:language>
                <v1:description>Turkish</v1:description>
         </v1:GroupDetail>
         <v1:GroupDetail>
                <v1:language>Fr</v1:language>
                <v1:description>French</v1:description>
         </v1:GroupDetail>
        </v1:GroupDetails> 
        <v1:Status>true</v1:Status>
        <v1:Parent>45434555</v1:Parent>
      </v1:Group>
    </v1:ProcessDistr>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

Output received with another set of Group details and also 'messageId' is missing.

Output received:

<?xml version="1.0" encoding="UTF-8"?>
<ProcessDistr >
  <Group >
    <GroupID>437848</GroupID>
     <GroupDetails>
     <GroupDetail>
            <language>De</language>
            <description>Deutsch</description>
     </GroupDetail>
     <GroupDetail>
            <language>En</language>
            <description>English</description>
     </GroupDetail>
      <GroupDetail>
            <language>Tu</language>
            <description>Turkish</description>
     </GroupDetail>
     <GroupDetail>
            <language>Fr</language>
            <description>French</description>
     </GroupDetail>
   </GroupDetails>
    <Status>true</Status>
    <Parent>45434554</Parent>
  </Group>
  <Group >
    <GroupID>437849</GroupID>
    <GroupDetails>
     <GroupDetail>
            <language>De</language>
            <description>Deutsch</description>
     </GroupDetail>
     <GroupDetail>
            <language>En</language>
            <description>English</description>
     </GroupDetail>
      <GroupDetail>
            <language>Tu</language>
            <description>Turkish</description>
     </GroupDetail>
     <GroupDetail>
            <language>Fr</language>
            <description>French</description>
     </GroupDetail>
   </GroupDetails>
    <Status>true</Status>
    <Parent>45434555</Parent>
  </Group>
  <messageId/>
</ProcessDistr>

This is XSLT code which I developed

Used XSLT code:

 <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" xmlns:prof="http://ixult.net/ProfileExchange" 
    xmlns:sap="http://www.sap.com/sapxsl" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsltc="http://xml.apache.org/xalan/xsltc" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:v1="http://xmldefs. ag.com/Applications/eer/V1" 
    xmlns:vwsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ns2="http://xmldefs.vag.com/DD/Commons" 
    exclude-result-prefixes="vwsu v1 ns2 xsi wsa" xmlns:wsa="http://www.w3.org/2005/08/addressing">
       <!-- Output -->
       <xsl:output encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="yes"/>
       <xsl:strip-space elements="*"/>

                       <xsl:template match="/">


              <xsl:element name="ProcessDistr">
               <xsl:for-each select="//soap:Body/v1:ProcessDistr/v1:Group">
                <xsl:element name="Group">

                    <xsl:element name="GroupID"><xsl:value-of select="v1:GroupID"/></xsl:element>
                     <xsl:element name="GroupDetails">
                        <xsl:for-each select="//v1:GroupDetails/v1:GroupDetail">
                         <xsl:element name="GroupDetail">

                         <xsl:element name="language"><xsl:value-of select="v1:language"/></xsl:element>
                         <xsl:element name="Description">

                          <xsl:value-of select="v1:Description"/></xsl:element>

                         </xsl:element>
                       </xsl:for-each>

                    </xsl:element>

                   <xsl:element name="Status"><xsl:value-of select="v1:Status"/></xsl:element>
                  <xsl:element name="Parent"><xsl:value-of select="v1:Parent"/></xsl:element>

                </xsl:element>                
              </xsl:for-each>
              <xsl:element name="messageId"><xsl:value-of select="wsa:messageID"/>
    </xsl:element>
             </xsl:element>


                    </xsl:template>



    </xsl:stylesheet>

Output expected:

    <?xml version="1.0" encoding="UTF-8"?>
    <ProcessDistr >
      <Group >
        <GroupID>437848</GroupID>
         <GroupDetails>
         <GroupDetail>
                <language>De</language>
                <description>Deutsch</description>
         </GroupDetail>
         <GroupDetail>
                <language>En</language>
                <description>English</description>
         </GroupDetail>
       </GroupDetails>
        <Status>true</Status>
        <Parent>45434554</Parent>
      </Group>
      <Group >
        <GroupID>437849</GroupID>
        <GroupDetails>
          <GroupDetail>
                <language>Tu</language>
                <description>Turkish</description>
         </GroupDetail>
         <GroupDetail>
                <language>Fr</language>
                <description>French</description>
         </GroupDetail>
       </GroupDetails>
        <Status>true</Status>
        <Parent>45434555</Parent>
      </Group>
      <messageId>04383-34380-3439939</messageId>
    </ProcessDistr>

Please help me on this code

Thank you very much.

With Best Regards, Sateesh N

4
  • Your code produces errors: xsltfiddle.liberty-development.net/6r5Gh3Z Commented Jul 4, 2019 at 12:05
  • Now I corrected my code Michael Commented Jul 4, 2019 at 12:15
  • Hi Michael, please check my code looks fine to execute Commented Jul 4, 2019 at 12:42
  • Please help me on this code Commented Jul 4, 2019 at 14:49

2 Answers 2

1

The main problem of the provided code is not on line 20, although fixing this helps get the wanted output.

The main problem is that the code doesn't make use of the powerful XSLT processing model, using which the solution can be expressed without any <xsl:for-each> instructions. Also, the code can be shrunk and made more compact, understandable and maintainable.

Here is a starter solution (30 lines) that shows how this can be done:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:v1="http://xmldefs. ag.com/Applications/eer/V1" xmlns:wsa="http://www.w3.org/2005/08/addressing"
  exclude-result-prefixes="soap-env wsa v1">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

  <xsl:template match="/*">
    <xsl:apply-templates select="soap-env:Body"/>
  </xsl:template>

  <xsl:template match="v1:ProcessDistr">
    <ProcessDistr>
      <xsl:apply-templates/>
      <xsl:apply-templates select="/*/soap-env:Header/wsa:messageId"/>
    </ProcessDistr>
  </xsl:template>

  <xsl:template match="v1:* | wsa:*">
   <xsl:element name="{local-name()}">
     <xsl:apply-templates select="node()|@*"/>
   </xsl:element>
  </xsl:template>
  <xsl:template match="soap-env:*"><xsl:apply-templates/></xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:v1="http://xmldefs. ag.com/Applications/eer/V1" xmlns:wsa="http://www.w3.org/2005/08/addressing">
    <SOAP-ENV:Header>
        <wsa:messageId>04383-34380-3439939</wsa:messageId>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
        <v1:ProcessDistr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <v1:Group xmlns:ns2="http://xmldefs.ag.com/DD/Commons">
                <v1:GroupID>437848</v1:GroupID>
                <v1:GroupDetails>
                    <v1:GroupDetail>
                        <v1:language>De</v1:language>
                        <v1:description>Deutsch</v1:description>
                    </v1:GroupDetail>
                    <v1:GroupDetail>
                        <v1:language>En</v1:language>
                        <v1:description>English</v1:description>
                    </v1:GroupDetail>
                </v1:GroupDetails>
                <v1:Status>true</v1:Status>
                <v1:Parent>45434554</v1:Parent>
            </v1:Group>
            <v1:Group xmlns:ns2="http://xmldefs.ag.com/DD/Commons">
                <v1:GroupID>437849</v1:GroupID>
                <v1:GroupDetails>
                    <v1:GroupDetail>
                        <v1:language>Tu</v1:language>
                        <v1:description>Turkish</v1:description>
                    </v1:GroupDetail>
                    <v1:GroupDetail>
                        <v1:language>Fr</v1:language>
                        <v1:description>French</v1:description>
                    </v1:GroupDetail>
                </v1:GroupDetails>
                <v1:Status>true</v1:Status>
                <v1:Parent>45434555</v1:Parent>
            </v1:Group>
        </v1:ProcessDistr>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

the wanted, correct result is produced:

<ProcessDistr>
   <Group>
      <GroupID>437848</GroupID>
      <GroupDetails>
         <GroupDetail>
            <language>De</language>
            <description>Deutsch</description>
         </GroupDetail>
         <GroupDetail>
            <language>En</language>
            <description>English</description>
         </GroupDetail>
      </GroupDetails>
      <Status>true</Status>
      <Parent>45434554</Parent>
   </Group>
   <Group>
      <GroupID>437849</GroupID>
      <GroupDetails>
         <GroupDetail>
            <language>Tu</language>
            <description>Turkish</description>
         </GroupDetail>
         <GroupDetail>
            <language>Fr</language>
            <description>French</description>
         </GroupDetail>
      </GroupDetails>
      <Status>true</Status>
      <Parent>45434555</Parent>
   </Group>
   <messageId>04383-34380-3439939</messageId>
</ProcessDistr>

XSLT is a wonderful language and when its full power is used it gives us short and elegant solutions. There are good learning resources waiting to be uncovered.

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

1 Comment

@Sateesh, You are welcome. You can check the "xslt" tag description for learning resources: stackoverflow.com/tags/xslt/info -- at the very end of this page
0

The main problem with your XSLT is this (line 20):

<xsl:for-each select="//v1:GroupDetails/v1:GroupDetail">

A path that starts with // selects all descendants of the root node, regardless of the current context. You only want to process the descendants of the current v1:Group, so you need to change it to:

<xsl:for-each select="v1:GroupDetails/v1:GroupDetail">

Note also that XML is case-sensitive:

<xsl:value-of select="v1:Description"/>

will not return the value of an element named v1:description.


I would also recommend using literal result elements instead of the xsl:element instruction. Use xsl:element when the name of the element needs to be determined at runtime.

3 Comments

Ok, Michale, looping problem resolved with your code and description will change. how to generates 'messageId' value from 'SOAP-Env:Header/wsa:messageId'
This is where an absolute path starting with // could be actually useful. Or use the full path soap:Envelope/soap:Header/wsa:messageId.
I should say you are James Bond. You solved all my problems simple way:) thank you so much

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.