[SalesForce] the correct JSON/Apex Strategy

Summary

I'm having a lot of trouble deserializing a JSON file into apex. The problem is not in how to deserialize a JSON string, but rather what type of apex objects I should be using. I don't come from a strongly typed background, so this issue (ironically, perhaps) is always confusing for me.

My JSON file looks like this:

{
    "digest":[
        {
            "id": "123",
            "name": "",
            "email": "",
            "sendEmailBool": true,
            "Opportunities":[
                {
                    "id": "opp123",
                    "name": "",
                    "NewChatters":[
                        {
                            "author": "",
                            "message": ""
                        }
                    ],
                    "NewTasks":[
                        {
                            "type": "",
                            "subject": ""
                        }
                    ],
                    "UpdatedTasks": [
                        {
                            "type": "",
                            "subject": "",
                            "status": ""
                        }
                    ],
                    "CompletedTasks": [
                        {
                            "type": "",
                            "subject": "",
                            "completedBy": ""
                        }
                    ]
                }
            ]
        },
        {
            "id": "456",
            "name": "",
            "email": "",
            "sendEmailBool": true,
            "Opportunities":[
                {
                    "id": "opp123",
                    "name": "",
                    "NewChatters":[
                        {
                            "author": "",
                            "message": ""
                        }
                    ],
                    "NewTasks":[
                        {
                            "type": "",
                            "subject": ""
                        }
                    ],
                    "UpdatedTasks": [
                        {
                            "type": "",
                            "subject": "",
                            "status": ""
                        }
                    ],
                    "CompletedTasks": [
                        {
                            "type": "",
                            "subject": "",
                            "completedBy": ""
                        }
                    ]
                }
            ]
        }
    ]
}

I'd love for this to just be a map(string, string), but of course this will deserialize to map(string, object). That's fine, but let's say I save this to some var:

Map<string, object> m = (Map<string, object>)JSON.deserializeUntyped(strBody);

object o = m.get('digest');

What do I do with 'o' now? I've tried looking up how to work with 'object' in apex, but if you try googling it, you get nothing. It's like we have an object class but there's nothing you can do with it.

Question One

What can I do with this object o? Is there a way that I can further retrieve the other data fields? How about modify them? There doesn't seem to be any getter/setter methods on it…

A different approach

Instead of trying to make this JSON file work, perhaps I can build a JSON file from the ground up.

As you see from the JSON, this is mostly just lists of sObjects. Perhaps I can just create map(string, sObject) and map(string, map(string,sobject)) and json serialize that. But this is where the strongly typed issue gets in the way.

As you can see, the JSON file has both sObjects and strings. For instance:

{
                "id": "456",
                "name": "",
                "email": "",
                "sendEmailBool": true,
                "Opportunities":[
                    {
                        "id": "opp123",
                        "name": ""
                    }
}

We have strings, boolean and an Opportunity sObject all in one.

Question 2

Assuming there isn't much to do with the solution from Question 1, how can I approach this issue from building the JSON from the ground up? What is the typical method?

Thank you all for your help!

PS. I saw a response to this question: How to deserialize a JSON String to Apex. The highest voted answer suggested using a tool call json2apex. Looking at that, it just seemed like a quick hack instead of the elegant solution. Perhaps this is the solution?

Best Answer

If you know the structure of the JSON before you get it then the easiest way is to create a class to define the Object.

For Example, given the following JSON String

{
    "id" : "someID",
    "street" : "someStreet",
    "listOfClass" : [
              {
                 "myVar" : "test"
              }
            ]
    "myArray" : ["1","2","3"]
}

Define the object as such:

public class myJSONClass{

    public string id;
    public string street;
    public listOfClass[] listOfClass;
    public String[] myArray;

    public class listOfClasses{
       public String myVar;
    }

}

Do not think of it as a class to clutter your other classes, think of it as an object definition (what it is)

Then simply deserialize as follows:

myJSONClass obj = (myJSONClass)JSON.deserialize(JSONSTRING,myJSONClass.class);

Where it becomes tricky is when you are using reserved names or an unknown structure.

In most of those cases you need to use the JSON Parser methods to parse the string.The exception is if you only have a FEW reserved names you can replace them as such

JSONString = JSONString.replace('name','name_Z');

then define the property in the class as name_Z. It will save a lot of work.

Back to your original point, if you have no class you can deserialize as such:

Map<String,Object> obj = JSON.deserializeUntyped(JSONString);

Then you can just cast everything to strings:

Map<String,String> results = New Map<String,String>();
for(String key : obj.keySet()){
   results.put(key,string.valueOf(obj.get(key));
}

However in this case you lose the structure. For example, in your string if you have lists, arrays, etc, you get the string representation of the values and not the actual structure. More useful to define a class and work with it.

Note You do not have to have everything defined in the class to deserialize unless you use deserializeStrict. So if in my first example you removed public String id it would still deserialize

In all the above examples, make sure to check for nulls and errors

Related Topic