Forum Discussion

Testranger's avatar
Testranger
Contributor
12 years ago

Renaming request nodes

Hi all,

While using property transfer, with xpath, i am able to forward nodes and children to the next request object. Problem is, my source node name needs to appear differently in my destination. I need to be able to rename the node name in the destination after the property transfer. Is this possible? Is this a groovy candidate?

Thanks in advance,
Testranger

4 Replies

  • M_McDonald's avatar
    M_McDonald
    Super Contributor
    One way to do it:

    Response XML
    <info>
    <item index="1">
    <itemContent index="1">asdfasd</itemContent>
    <itemContent index="2">wertywe</itemContent>
    </item>
    <item index="2">
    <itemContent index="1">ae344qwer</itemContent>
    <itemContent index="2">w64534</itemContent>
    </item>
    </info>

    Groovy script to rename the first node (adapted from http://stackoverflow.com/a/4956151:)
    import javax.xml.transform.Transformer
    import javax.xml.transform.TransformerFactory
    import javax.xml.transform.OutputKeys
    import javax.xml.parsers.DocumentBuilderFactory
    import javax.xml.parsers.DocumentBuilder
    import org.w3c.dom.Document
    import org.xml.sax.InputSource
    import javax.xml.transform.dom.DOMSource
    import javax.xml.transform.stream.StreamResult

    def gu = new com.eviware.soapui.support.GroovyUtils( context )
    def resp = context.expand( '${HTTP Test Request#Response#/info/item[1]}' )

    // parse String as DOM
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(new InputSource(new StringReader(resp)));

    // modify DOM
    doc.renameNode(doc.getDocumentElement(), null, "myItem");

    // output DOM as String
    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    StringWriter sw = new StringWriter();
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.transform(new DOMSource(doc), new StreamResult(sw));
    def newNode = sw.toString();
    return newNode

    Request with property expansion:
    <newInfo>${Groovy Script#result}</newInfo>

    Expanded request with renamed node:
    <newInfo>
    <myItem index="1">
    <itemContent index="1">asdfasd</itemContent>
    <itemContent index="2">wertywe</itemContent>
    </myItem>
    </newInfo>
  • Hi M McDonald,

    Thank you for the response. However, was hoping this could be done by manipulating a request object with data already in it. I.e. take the node segment, loop through and rename, and then persist changes.

    I tried the following....

    def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )
    def requestHolder = groovyUtils.getXmlHolder("updateRequest#Request")
    def requestNode = requestHolder.getDomNode("//*:updateRequest[1]")
    def requestDoc = requestHolder.getDomNode("//*:updateRequest[1]/ns3:valuation").getOwnerDocument()
    for (int i = 0; i < requestDoc.getLength(); i++) {
    def node = requestDoc.item(i)
    requestDoc.renameNode(node, requestNode.getNamespaceURI(), "ns22:data")
    }
    requestHolder.updateProperty(true)

    Ended with "java.lang.RuntimeException: DOM Level 3 Not implemented" error.

    Any ideas?

    Regards,
    Testranger
  • M_McDonald's avatar
    M_McDonald
    Super Contributor
    OK, this one changes matching nodes in an existing request:
    import javax.xml.transform.Transformer
    import javax.xml.transform.TransformerFactory
    import javax.xml.transform.OutputKeys
    import javax.xml.parsers.DocumentBuilderFactory
    import javax.xml.parsers.DocumentBuilder
    import org.w3c.dom.Document
    import org.xml.sax.InputSource
    import javax.xml.transform.dom.DOMSource
    import javax.xml.transform.stream.StreamResult
    import org.apache.xpath.XPathAPI
    import org.apache.xml.dtm.ref.DTMNodeList

    def request = context.expand( '${Test Request#Request}' )

    Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(request)));
    DTMNodeList nodes = XPathAPI.selectNodeList(doc.getFirstChild(), "/info/item");
    for (node in nodes) {
    doc.renameNode(node, null, 'myItem')
    }

    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    StringWriter sw = new StringWriter();
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.transform(new DOMSource(doc), new StreamResult(sw));
    def newRequest = sw.toString();
    testRunner.testCase.getTestStepByName('Test Request').httpRequest.requestContent = newRequest
  • Hi McDonald,

    That script has got me further. However, i need the name spaces of the nodes being changed, changed as well. This was not straight forward when I tried implementing that functionality.
    However, your script did give me ideas. Thanks

    I have managed to get functionality required using a hack - now that I know how to set the request object after manipulation is done.

    I treat the request as a plain string and call replaceAll.

    def request = context.expand( '${updateRequest#Request}' ).toString()
    request = request.replaceAll("ns3:valuation", "ns22:data")
    def tc = testRunner.testCase;
    tc.getTestStepByName('updateRequest').httpRequest.requestContent = request
    def result = null //to stop the groovy information pop-up

    This will get me over the line. I am aware of the risk of other data being changed as a result, but I am only changing namespace:nodename type combos. So, other data should be relatively safe. Please advise if there is any other risk or problem with doing it this way.

    Thanks again, much appreciated,
    TestRanger