18

Any thoughts on why the REST web service below does not properly deserialize the input argument?

Unit test passes. CURL request (below) returns error.

@RestResource(urlMapping='/test/*')
global with sharing class TestService {

    @HttpPost
    global static string doPost(TestRequest req){
        return req.testArg;
    }

    private static testMethod void tests(){
        TestRequest req = new TestRequest();
        req.testArg = 'hello';
        String result = TestService.doPost(req);
        System.assertEquals('hello', result);
    }

    global class TestRequest{
        global string testArg;
    }
}

Test Request

curl -X POST -H 'Authorization: Bearer sessionID' 
-H 'Content-Type: application/json' 
-d '{"testArg": "testValue"}' 
'https://power-site-5184.database.com/services/apexrest/test'

Response

[{"message":"Unexpected parameter encountered during deserialization: testArg 
at [line:1, column:14]","errorCode":"JSON_PARSER_ERROR"}]

Update (resolved)

The correct JSON data structure should be

-d '{"req": {"testArg": "testValue"}}'
17
{
"req": {
    "testArg": "testValue"
}
}

The request data should be as shown above .

I had blogged on this topic may be that can be helpful to you

http://cloudyworlds.blogspot.in/2012/11/native-parsing-of-json-input-into-user.html

The deserialization happens with parameter name in sfdc.

Also just out of topic but its better to keep Test class in separate class as per new sfdc guidelines for test class

SFDC Documentation Link:

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_rest_methods.htm#ApexRESTUserDefinedTypes

  • Mohith - That worked. Thanks! I'll ping some SForce Evangelists to request documentation update. (btw- v28 unit test isolation observed in prod. Inline tests here for brevity). – dlog Aug 10 '13 at 5:17
  • @dlog Always happy to help.SFDC has documented that and may be we missed it .I have added link to sfdc documentation in my answer .:)Thanks for upvote and accepting answer . – Mohith Shrivastava Aug 10 '13 at 5:31
9

Alternatively you can control the deserialization explicitly to get your original request to work.

@RestResource(urlMapping='/test/*')
global with sharing class TestService {

    @HttpPost
    global static string doPost(){
        String requestBody = RestContext.request.requestBody.toString();
        TestRequest req = (TestRequest) JSON.deserialize(requestBody, TestRequest.class);
        return req.testArg;
    }

    private static testMethod void tests(){
        TestRequest req = new TestRequest();
        req.testArg = 'hello';

        RestContext.request = new RestRequest();
        RestContext.request.requestBody = Blob.valueOf(JSON.serialize(req));

        String result = TestService.doPost();
        System.assertEquals('hello', result);
    }

    global class TestRequest{
        global string testArg;
    }
}

Test Request

curl -X POST -H 'Authorization: Bearer <sessionID>' 
-H 'Content-Type: application/json' 
-d '{"testArg": "hello"}' 
https://<instance url>/services/apexrest/test

Result

"hello"

By explicitly calling deserialize you can also accept json requests with unknown fields. Which will just be silently ignored.

Test Request

curl -X POST -H 'Authorization: Bearer <sessionID>' 
-H 'Content-Type: application/json' 
-d '{"testArg": "hello", "unknown_value": 42}' 
https://<instance url>/services/apexrest/test

Result

"hello"
  • 1
    Thanks for posting this! The deserializing approach/syntax on the SFDC side was what I was needing! – Chris Emerson Feb 21 '18 at 13:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.