Handling Multiple For Loop in XSLT

2019-07-25 14:35发布

问题:

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

回答1:

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.



回答2:

Please help me on this code

Dear Satish,

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.



标签: xslt