25

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"}}'

3 Answers 3

23
{
"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

2
  • 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
    Commented Aug 10, 2013 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 . Commented Aug 10, 2013 at 5:31
17

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
  • 3
    Thanks for posting this! The deserializing approach/syntax on the SFDC side was what I was needing! Commented Feb 21, 2018 at 13:38
0

To add on top of the accepted answer: make sure the method signature is aligned with the json body i.e.:

@HttpPost
global static void doPost(String x, Integer y) {}

matches to:

{
    "x": "abc",
    "y": 123
}

And also, make sure Content-Type is set to application/json

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .