Forum Discussion

richie's avatar
richie
Community Hero
7 years ago

Grab value within XML fragment within a single XML element (CDATA element query)?

Hey!

 

 

I'm posting a SOAP request to a web service which generates a SOAP response.  The XML payload within the SOAP response is all contained within 1 tag (using a CDATA element)

 

Within the payload there is a unique identifier that I need to transfer to a subsequent JDBC test step as the filter in the query - see as follows:

 

 

Datasource (directory type) with fileContents property
SOAP request (sourcing the fileContents property as the payload of the request) <-- need to do a PROPERTY TRANSFER on a specific value in the payload 
JDBC Test Step (using the uniqueID from the SOAP response)

When I've done this before - the response wasn't in a SOAP wrapper - so all the tags/attributes within the Response's XML were on different lines in the Response - so it was straightforward to do a PROPERTY TRANSFER on to transfer the uniqueID from the response.

 

HOWEVER - because all the payload of the XML response is contained within a single SOAP payload tag (using the CDATA element) - I'm struggling to extract the uniqueID (SyncID in the XML below)

 

<SOAP-ENV:Envelope>
   <SOAP-ENV:Body>
      <NS1:SyncResponse>
         <return><![CDATA[<SyncVenueOutput>
  <Responses>
    <SyncSuccesses>
      <SyncSuccess SyncID="AB123"/>
    </SyncSuccesses>
    <SyncIgnores>
      
    </SyncIgnores>
    <SyncErrors>
      
    </SyncErrors>
  </Responses>
</SyncOutput>]]></return>
      </NS1:SyncResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

As I say above - I need to extract the SyncID value (currently set above as 'AB123' above) to pass to the JDBC test step - but when I try the transfer - I'm transferring ALL the XML - not just the 'AB123'

 

 

I hope I've been clear,

 

Can anyone provide any guidance please?

 

Many thanks to all!

 

richie

23 Replies

  • groovyguy's avatar
    groovyguy
    Champion Level 1

    I merged your two threads together since they are so closely related. For doing the property transfer, you are going to probably need to use a groovy script as well due to having to manually parse the CDATA tag(s).

     

    import com.eviware.soapui.support.XmlHolder
    
    def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
    
    // get the response, specifically the //RETURN element that contains DATA tags
    def response = context.expand( '${TESTTSTEP#Request#declare namespace NS1=\'urn:SyncServicesIntf-ISyncServices\'; //NS1:SyncResponse[1]/return[1]}' )
    
    // remove the CDATA tags
    response = response.replaceAll( "<!\\[CDATA\\[", "" )
    response = response.replaceAll( "]]>", "" )
    
    // Parse the response into XML
    def holder = groovyUtils.getXmlHolder( response )
    
    // Parse the XML of the cdata for the attribute
    def successID = (holder["//Responses/SyncSuccesses/SyncSuccess/@SyncID"]);
    
    // set the property in a property test step
    properyTestStepName.setPropertyValue("SyncID", successID);
    

     

    • richie's avatar
      richie
      Community Hero

      nice one to groovyguy

       

      I made some small updates and its working (see below)

       

      import com.eviware.soapui.support.XmlHolder
      def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
      // get the response, specifically the //RETURN element that contains DATA tags. This may need to be adjusted to fit your own project, but you want to collect the //return element
      def response = context.expand( '${SOAP Request#Response#declare namespace NS1=\'urn:CSMVenueSyncServicesIntf-ICSMVenueSyncServices\'; //NS1:SyncVenueResponse[1]/return[1]}' )
      // remove the CDATA tags
      response = response.replaceAll( "<!\\[CDATA\\[", "" )
      response = response.replaceAll( "]]>", "" )
      // Parse the response into XML
      def holder = groovyUtils.getXmlHolder( response )
      log.info((holder["exists(//Responses/VenueSyncSuccesses/VenueSyncSuccess)"]));
      log.info(holder["count(//Responses/VenueSyncErrors/VenueSyncError)"]);
      def successExists = (holder["exists(//Responses/VenueSyncSuccesses/VenueSyncSuccess)"]);
      def errorCount = (holder["count(//Responses/VenueSyncErrors/VenueSyncError)"]).toInteger();
      assert (successExists && errorCount == 0);

       

      I've placed the groovy step immediately after my SOAP Request step and it generates 2 rows in the logging as follows:

       

      So - the above is logging the info fine - returns 2 rows depending on the presence of VenueSyncSuccess & VenueSyncError tags are present

       

       

       

       

      I can see the 'assert (successExists && errorCount == 0);' line - can you confirm what they are actually doing?

       

      FINALLY - I need to create an ASSERTION on the above somehow to indicate during execution whether the VenueSyncSuccess is present or not (to prove the request was successful)

       

      I have both positive and negative tests that will generate either a VenueSyncSuccess tag or a VenueSyncError tag - I need to (based on the purpose of the test) assert whether these are present or not - can you please advise?

       

      I raised a separate query a while back about passing logged info to a PROPERTY test step - would this allow me to assert on the values?  

       

      I'm sorry - I avoid groovy just because I dont know where to start (I'm the only tester where I work - so it;s not like I can ask anybody else!)

       

      Many thanks - you're really saving my life here!

       

      richie

       

  • groovyguy's avatar
    groovyguy
    Champion Level 1
    The assert statement that you called out is asserting that there is a success and that there is no error. That statement makes the groovy script act like an assertion.
    • groovyguy's avatar
      groovyguy
      Champion Level 1

      Without getting into the nitty gritty details (which can be found here), an assert statement is like an assertion on a Soap/REST Test Step. It is verifying a certain piece of information and making the groovy test step pass or fail accordingly. 

      • richie's avatar
        richie
        Community Hero

        I gotcha - the groovy step will go 'red' if it fails - right?

         

        I don't know if you noticed, but I had a second 'failed' option (VenueSyncIgnore as well as the VenueSyncError) within the cdata - I've altered your script as follows (totally ripping off what you've already done) and the results appear to work - script is below:

         

        import com.eviware.soapui.support.XmlHolder
        def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
        // get the response, specifically the //RETURN element that contains DATA tags. This may need to be adjusted to fit your own project, but you want to collect the //return element
        def response = context.expand( '${SOAP Request#Response#declare namespace NS1=\'urn:CSMVenueSyncServicesIntf-ICSMVenueSyncServices\'; //NS1:SyncVenueResponse[1]/return[1]}' )
        // remove the CDATA tags
        response = response.replaceAll( "<!\\[CDATA\\[", "" )
        response = response.replaceAll( "]]>", "" )
        // Parse the response into XML
        def holder = groovyUtils.getXmlHolder( response )
        log.info((holder["exists(//Responses/VenueSyncSuccesses/VenueSyncSuccess)"]));
        log.info(holder["count(//Responses/VenueSyncErrors/VenueSyncError)"]);
        log.info(holder["count(//Responses/VenueSyncIgnores/VenueSyncIgnore)"]);
        def successExists = (holder["exists(//Responses/VenueSyncSuccesses/VenueSyncSuccess)"]);
        def errorCount = (holder["count(//Responses/VenueSyncErrors/VenueSyncError)"]).toInteger();
        def ignoreCount = (holder["count(//Responses/VenueSyncIgnores/VenueSyncIgnore)"]).toInteger();
        assert (successExists && errorCount == 0 && ignoreCount ==0);

        OK - if you're feeling in a helpful mood I have 1 more point and all I'll stand by all previous conversations

         

        I need to be able to extract the VenueID attribute value from within the cdata string to pass this onto a subsequent JDBC test step.  Essentially, once the successful request has been posted, I need to run a query to retrieve the record from within the DB to verify certain attributes have been updated.

         

        I should highlight - the VenueID attribute will 'move' in the response, relative to whether the request is successful or is ignored (for certain error scenario) or is a failure (most severe failed options).

         

        Obviously I only care about when the request has been successful as only successful requests will write to the DB.

         

        I can extract the value and pass it onto the JDBC test step that'll be me very happy!

         

         

        Cheers!

         

        richie