0

I have an Apex REST endpoint that takes an SObject parameter but I can't figure out how I can pass in nested objects. Is this possible out of the box?

Let's say my endpoint's code looked like this:

@HttpPatch global static void upsertAccountAndInsertOrders(Account account) {
    // The contents of this method are just for illustration.
    // The code in here is irrelevant and is never run because
    // the Apex REST endpoint magic tries to parse the incoming
    // request into an Account object but throws an error when 
    // encountering the nested data.
    for (Order__c order : account.Orders__r) {
        // There would be a list or Order__c objects in the JSON
    }
    insert account.Orders__r;
    upsert account;
}

I'd want to pass in JSON like this:

{"account": {"ExternalId__c": 123, "Orders__r": [{"Name": "Order to insert"}]}}

However, I get this response from the API (column number removed as it doesn't align with my simplified example):

HTTP 400 Bad Request
[{"message":"Expected JSON object to deserialize apex parameter from at [line:1, column:###]","errorCode":"JSON_PARSER_ERROR"}]

While this is just a simple example, I have deeply nested trees of objects with multiple master-detail relationships that I'd like to pass in to my Apex REST API, so it'd be ideal if I didn't have to write any additional boilerplate to make this work.

In looking at the output of JSON.serialize for a similar object, I saw that there was a records key, so I've tried this as well:

{"account": {"ExternalId__c": 123, "Orders__r": {"records": [{"Name": "Order to insert"}]}}}

A request like this won't return an HTTP 400, but account.Orders__r.isEmpty() (is true) and there are no records once the Apex runs.

Is it impossible to write deserialize JSON into related fields?

4

This seems to work both ways using the body of the RestRquest.

Rest Resource Class

@RestResource(urlMapping='/restsObject')
global class exampleRestClass {

    @HttpPost
    global static sObject doPost() {
        Account a = (Account)json.deserialize(RestContext.request.requestBody.toString(),Account.class);
        System.debug(a);
        system.debug(a.contacts);

        return a;
    }
}

Exec Anon Code

//Make sure we get an account with contacts
Contact tmp = [Select Id, AccountId From Contact Where AccountId != null limit 1];
Account a = [Select Id, Name, (Select FirstName, LastName From Contacts) From Account Where Id = :tmp.AccountId];


HttpRequest req = New HttpRequest();
req.setEndpoint('https://{instanceurl}/services/apexrest/restsObject');
req.setMethod('POST');
req.setHeader('Authorization','Bearer ' + userInfo.getSessionId());
req.setBody(json.serialize(a));
req.setHeader('Content-Type','application/json');

httpresponse resp = New HTTP().send(req);

Account acc = (Account)json.deserialize(resp.getBody(),Account.class);
system.debug(acc);
System.debug(acc.Contacts);

Rest class Debug

21:47:30.0 (5124568)|USER_DEBUG|[13]|DEBUG|Account:{Id=0013600000YkWKYAA3, Name=mytest}

21:47:30.0 (5393203)|USER_DEBUG|[14]|DEBUG|(Contact:{AccountId=0013600000YkWKYAA3, FirstName=Test, Id=0033600000prcBKAAY, LastName=Contact}, Contact:{AccountId=0013600000YkWKYAA3, FirstName=First, Id=0033600000vYfTeAAK, LastName=Last})

Exec Anon Debug

21:53:35.31 (268866459)|USER_DEBUG|[16]|DEBUG|Account:{Id=0013600000YkWKYAA3, Name=mytest}

21:53:35.31 (269053369)|USER_DEBUG|[17]|DEBUG|(Contact:{AccountId=0013600000YkWKYAA3, FirstName=Test, Id=0033600000prcBKAAY, LastName=Contact}, Contact:{AccountId=0013600000YkWKYAA3, FirstName=First, Id=0033600000vYfTeAAK, LastName=Last})

3
  • Awesome! Looks like that goes through a different code path so it works. Basically, for a master-detail relationship, the key is that you need to add something like with totalSize, and done, otherwise this won't work. {"RelatedObjects__r": {"totalSize": 2, "done": true, "records": [{...}, {...}]}} – jon_wu May 18 '17 at 2:32
  • @jon_wu - so my answer does or does not work with MD relationships? – Eric May 18 '17 at 2:34
  • 1
    Your answer works, but my comment was just clarifying that you have to add totalSize, done, records keys under a related objects when you're making the JSON. Normally, you wouldn't be making the JSON from Salesforce, so the natural thing to do would be to have a list of related objects like this: {"RelatedObjects__r":[{...}, {...}]}. Not only do you need to have the list underneath a records key, but you also need to add in totalSize and done: true. – jon_wu May 18 '17 at 3:04

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.