Question

I'm trying to parse XML in a Grails controller - I can successfully parse the result of a GET but when receiving a PUT, I can't get the values out of the request. Code follows.

Test: (PUTs a dummy Person so that I can test the parsing and saving)

import grails.test.mixin.*
import grails.test.mixin.domain.DomainClassUnitTestMixin
import org.junit.*
import com.mycompany.stuff.Person

@TestFor(ServiceController)
@TestMixin(DomainClassUnitTestMixin)
class ServiceControllerTests {
    void testCreateWithXML() {
        mockDomain(Person)
        request.method = "PUT"
        def controller = new ServiceController()
        controller.request.contentType = 'text/xml'
        controller.request.content = '''
                <person>
                    <refId>123-abc</refId>
                    <otherThing>some stuff</otherThing>
                </person>
                '''.stripIndent().getBytes() // note we need the bytes (copied from docs)
        def response = controller.create()
        assert Person.count() == 1
        assertEquals "123-abc", Person.get("123-abc").id
    }
}

Controller: Receives the put (correctly) after being mapped to the create method.

class ServiceController {
...
    def create() {
        if (request.format != "xml") {
            render 406 // Only XML expected
            return
        }

        def requestBody = request.XML
        def objectType  = requestBody.name() as String
        log.info "Received ${objectType} - ${requestBody}"
        if (!(objectType.toLowerCase() in ['person','personsubtype']))
        {
                render (status: 400, text: 'Unknown object type received in PUT')
                return
        }

        def person      = new Person(id: requestBody.person.refId.text())
        person.save()

        log.info "Saved ${person}"
        render 200
    }

Using the debugger, I can see that when the request is received, the variable requestBody is received as a NodeChild, and the name() is correct. I can also see that the requestBody.person.refId variable's metaClass is of GPathResult... yet the .text() (and .toString()) always return null. The first log.info prints the output:

2013-07-09 20:04:07,862 [main] INFO  client.ServiceController  - Received person - 123-abcsome stuff

so I know that the contents came across.

Any and all suggestions appreciated. I've been trying this for some time and am at my wits' end.

Was it helpful?

Solution

You are accessing refId inappropriately from requestBody. In your case <person> is itself represented by requestBody.

requestBody.refId.text() will give you 123-abc.

The controller implementation and the test case should be written this way:

def create() {
        if (request.format != "xml") {
            render 406 // Only XML expected
            return
        }

        def requestBody = request.XML
        def objectType  = requestBody.name() as String

        //You can see here objectType is person which signifies
        //requestBody is represented as the parent tag <person> 
        log.info "Received ${objectType} - ${requestBody}"
        if (!(objectType.toLowerCase() in ['person','personsubtype'])) {
            render (status: 400, text: 'Unknown object type received in PUT')
            return
        }

        //Since <person> is represented by requestBody, 
        //refId can be fetched directly from requestBody
        def person = new Person(id: requestBody.refId.text())
        person.save()

        log.info "Saved ${person}"
        render 200
    }

Test Class can be optimized and unwanted items can be removed:-

//Test Class can be optimized
import grails.test.mixin.*
import org.junit.*
import com.mycompany.stuff.Person

@TestFor(ServiceController)
//@Mock annotation does the mocking for domain classes
@Mock(Person)
class ServiceControllerTests {
    void testCreateWithXML() {
        //mockDomain(Person) //Not required, taken care by @Mock
        request.method = "PUT"
        //No need to initialize controller 
        //as @TestFor will provide controller.
        //def controller = new ServiceController()
        controller.request.contentType = 'text/xml'
        controller.request.content = '''
                <person>
                    <refId>123-abc</refId>
                    <otherThing>some stuff</otherThing>
                </person>
                '''.stripIndent().getBytes() // note we need the bytes (copied from docs)
        controller.create()

        assert controller.response.contentAsString == 200
        assert Person.count() == 1
        assertEquals "123-abc", Person.get("123-abc").id
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top