Demystifying JSON Parsing In Apex

JSON Parsing

Learn and demystify JSON parsing in Apex.

You got back a JSON string either as a response from a REST service, passed through from your visual force page via remote action, or passed from your lighting component to your apex controller, now how do you parse this information? For simple JSON structures Salesforce can automatically parse them for you. There are tools online that would generate apex classes from the JSON string that would assist in parsing the JSON input.

It is good to get back to the basics and understand a little bit how the JSON string is structured and how the parsing is done.

What is JSON? JSON is the acronym of Javascript Object Notation and is a lightweight format that is used for data exchange between servers and modern applicationsIt is based on a subset of JavaScript language (the way objects are built in JavaScript).

JSON structure comes in the following formats:

  • collection of name values based on the following data types
    • string
      • {"name" : "olopsman"}
    • number
      • {"age" : 42}
    • object (JSON object)
      • {"car" : {"model" : "outlander", "year" : "2016"}}
    • array
      • {"kids" : ["penny", "padma", "amber", "pauline"]}
    • boolean
      • {"married" : true}
    • null
      • {"entrepreneur" : null}
  • array of values or objects
    • ["web development","UX design","salesforce"]
    • [{"employment": "davanti"},{"employment":"cloud concept"}]

Having a good grasp of the JSON structure is key on knowing which Salesforce JSON class methods to use for parsing. 

JSON.deserializeUntyped(jsonString)
JSON.deserializeUntyped(jsonString) transforms the JSON key/values to a Salesforce map collection of primitive types

Run and execute as anonymous apex

String input = '{"name":"paulo","age":42, "car" : {"model" : "outlander", "year" : "2016"}}';
Map<String, Object> o = (Map<String, Object>) JSON.deserializeUntyped(input);
system.assertEquals(o.get('name'), 'paulo');
system.assertEquals(o.get('age'), 42);
Map<String, Object> car = (Map<String, Object>) o.get('car');
system.assertEquals(car.get('model'), 'outlander');

JSON.deserialize(jsonString, apexType)
JSON.deserialize(jsonString, apexType) – the apexType could be a custom object, SObject or a user-defined class.  With API version 35 and higher when JSON content has attributes not found in the custom apex class or fields on a custom object or Sobject,  deserialization it ignores those attributes and parses the JSON

Deserialized into standard object sample – only the name attribute can be mapped to the Account object, the rest are ignored.

Run and execute as anonymous apex

String input = '{"name":"paulo","age":42, "car" : {"model" : "outlander", "year" : "2016"}}';
Account a = (Account) JSON.deserialize(input, Account.class);
system.assertEquals(a.name, 'paulo');
system.debug(a);

Deserialized using an Apex class sample: – JSON attributes not defined in the Apex class will be ignored.

Create and save this class

public class GetPerson {
public String name {get;set;}
public Integer age {get;set;}
public Car car {get;set;}
public class Car {
public String model {get;set;}
public String year {get;set;}
}
}

Run and execute as anonymous apex

String input = '{"name":"paulo","age":42, "married":true, "car" : {"model" : "outlander", "year" : "2016"}}';
GetPerson person = (GetPerson) JSON.deserialize(input, GetPerson.class);
system.assertEquals(person.name, 'paulo');
system.assertEquals(person.car.model, 'outlander');

A slight variation, JSON string is an array of object. Run and execute as anonymous apex

String input = '[{"name":"paulo","age":42, "car" : {"model" : "outlander", "year" : "2016"}}, {"name":"tin","age":40, "car" : {"model" : "crv", "year" : "2004"}}]';
List<GetPerson> person = (List<GetPerson>) JSON.deserialize(input, List<GetPerson>.class);
system.assertEquals(person.size(), 2);

JSON.deserializeStrict(jsonString, apexType)
JSON.deserializeStrict(jsonString, apexType) – similar to above but the deserialization fails if those attributes are not found either as fields on the custom object or Sobject, or not defined in the custom apex class.

Run and execute as anonymous apex

String input = '{"name":"paulo","age":42, "car" : {"model" : "outlander", "year" : "2016"}}';
Account a = (Account) JSON.deserializeStrict(input, Account.class);

will throw this error

Line: 19, Column: 1
System.JSONException: No such column ‘age’ on Sobject of type Account

JSON.createParser(jsonString)
createParser(jsonString) – this would return the JSONParser class. This is useful for retrieving specific data(eg. access_token from oAuth response) without a need of an Apex class or if you want to parse the JSON to control how it maps to an Apex class.

Here is a slight variation of our example, an array of objects and one of the attributes is not a supported variable naming convention in Apex.

[
{
"_id": "1",
"name": "paulo",
"age": 42,
"car": {
"model": "outlander",
"year": "2016"
},
"kids": [
"penny",
"padma",
"amber",
"pauline"
]
},
{
"_id": "2",
"name": "tin",
"age": 40,
"car": {
"model": "crv",
"year": "2004"
},
"kids": [
"mary",
"sophie",
"patrice",
"laeticia"
]
}
]

Update the Apex class to the following:

public class GetPerson {
public String x_id {get;set;}
public String name {get;set;}
public Integer age {get;set;}
public List<String> kids {get;set;}
public Car car {get;set;}
public class Car {
public String model {get;set;}
public String year {get;set;}
}
}

Of course, you can always have other alternative solutions and options like the two below.

  • You can use the deserializeUntyped or deserialize with the Apex class and ignore the _id attribute if you do not need it on your application.
  • Run a String replace method on the JSON string to change the _id attribute to x_id, then use the deserialize method with the Apex class.

Or use the JSON.createParser to create a JSONParser object where you manually traverse the JSON token and values. Honestly using the parser is not that intuitive, I recommend at this point to look into online tools such as JSON2Apex for bit complex JSON structures. Kudos to the people behind this tool for making it available to us devs.

For the sake of this tutorial, let’s try using the JSONParser and the JSONToken Enum to parse this input.

Run the following on anonymous apex

String input = '[{"_id":"1","name":"paulo","age":42, "car" : {"model" : "outlander", "year" : "2016"}, "kids" : ["penny", "padma", "amber", "pauline"]}, {"_id":"2","name":"tin","age":40, "car" : {"model" : "crv", "year" : "2004"},"kids" : ["mary", "sophie", "patrice", "laeticia"]}]';
JSONParser parser = JSON.createParser(input);
List<GetPerson> gpList = new List<GetPerson>();
while(parser.nextToken() != JSONToken.END_ARRAY) { // we started with an array of objects
GetPerson gp = new GetPerson();
while(parser.nextToken() != JSONToken.END_OBJECT){ // loop through each object
if(parser.getCurrentToken() == JSONToken.FIELD_NAME) { //token should be field name
String attr = parser.getText(); //get the text of the field name
parser.nextToken(); // move the pointer
//start mapping the fields
if(attr == '_id') {
gp.x_id = parser.getText();
} else if(attr == 'name') {
gp.name = parser.getText();
} else if(attr == 'age') {
gp.age = parser.getIntegerValue();
} else if(attr == 'car') {
//create instance of the inner class car
GetPerson.Car gpcar = new GetPerson.Car();
//this is new object - make another while to loop through
while(parser.nextToken() != JSONToken.END_OBJECT) {
if(parser.getCurrentToken() == JSONToken.FIELD_NAME) {
String carAttr = parser.getText();
//move the pointer
parser.nextToken();
if(carAttr == 'model') {
gpcar.model = parser.getText();
} else if(carAttr == 'year') {
gpcar.year = parser.getText();
//finally assign the year assign the car
gp.car = gpcar;
}
}
}
} else if(attr == 'kids') { //this is array of values
List<String> kids = new List<String>();
//do another while
while(parser.nextToken() != JSONToken.END_ARRAY) {
kids.add(parser.getText());
}
gp.kids = kids;
}
}
}
gpList.add(gp);
}
system.assertEquals(gpList.size(), 2);
system.debug(gpList);

So what I did was using while statements I looped through the array of objects and traversed each token and mapped the attributes to the Apex Class.

Here is the final apex class.

public class GetPerson {
public String x_id {get;set;}
public String name {get;set;}
public Integer age {get;set;}
public List<String> kids {get;set;}
public Car car {get;set;}
public class Car {
public String model {get;set;}
public String year {get;set;}
}
public static List<getperson> parse(String jsonInput) {
JSONParser parser = JSON.createParser(jsonInput);
List<GetPerson> gpList = new List<GetPerson>();
while(parser.nextToken() != JSONToken.END_ARRAY) { // we started with an array of objects
GetPerson gp = new GetPerson();
while(parser.nextToken() != JSONToken.END_OBJECT){ // loop through each object
if(parser.getCurrentToken() == JSONToken.FIELD_NAME) { //token should be field name
String attr = parser.getText(); //get the text of the field name
parser.nextToken(); // move the pointer
//start mapping the fields
if(attr == '_id') {
gp.x_id = parser.getText();
} else if(attr == 'name') {
gp.name = parser.getText();
} else if(attr == 'age') {
gp.age = parser.getIntegerValue();
} else if(attr == 'car') {
//create instance of the inner class car
GetPerson.Car gpcar = new GetPerson.Car();
//this is new object - make another while to loop through
while(parser.nextToken() != JSONToken.END_OBJECT) {
if(parser.getCurrentToken() == JSONToken.FIELD_NAME) {
String carAttr = parser.getText();
//move the pointer
parser.nextToken();
if(carAttr == 'model') {
gpcar.model = parser.getText();
} else if(carAttr == 'year') {
gpcar.year = parser.getText();
//finally assign the year assign the car
gp.car = gpcar;
}
}
}
} else if(attr == 'kids') { //this is array of values
List<String> kids = new List<String>();
//do another while
while(parser.nextToken() != JSONToken.END_ARRAY) {
kids.add(parser.getText());
}
gp.kids = kids;
}
}
}
gpList.add(gp);
}
return gpList;
}
}

Call it from anonymous apex like so:

String input = '[{"_id":"1","name":"paulo","age":42, "car" : {"model" : "outlander", "year" : "2016"}, "kids" : ["penny", "padma", "amber", "pauline"]}, {"_id":"2","name":"tin","age":40, "car" : {"model" : "crv", "year" : "2004"},"kids" : ["mary", "sophie", "patrice", "laeticia"]}]';
List<GetPerson> gpList = GetPerson.parse(input);
system.debug(gpList);

Phew! That was a bit to cover but investing the time to learn how the JSON parsing is worth it.

Sample codes used in the tutorial can be found in my Github repo here. I’ll post a video tutorial soon.

Hit me up in the section below for comments and feedback.

12 thoughts on “Demystifying JSON Parsing In Apex

  1. This is an excellent practical guide on JSON parsing. Extremely helpful. I refer to this all the time.

  2. Really incredible lesson here. Thanks so much for doing this. Found you when searching for Deserializing JSON for Apex.

    I ended up creating my own class and then using a for loop to create my records from a callout.

  3. Thank you, appreciate the example and how you’ve highlighted that some keywords are reserved in Apex so can’t be used in JSON wrapper classes. We’re long-time fans of the JSON2Apex tool built on heroku but it had some shortcomings so decided to build our own version that has additional features. We’d love to get your feedback since you’re an expert on this topic! You can test-drive it here: https://www.ratedcalculator.com/software/json-to-apex-converter

Leave a Reply

Your email address will not be published. Required fields are marked *