Press ESC to close

Parse JSON by one line of code using Winter’12 JSON API !

Salesforce Winter’12 release came with a great new API addition to Apex stack i.e. JSONParser. This parser solves all the classic problems using open source Apex based JSONObject.cls, to know more about these problems read my previous post.

This post is an attempt to discuss how one can simplify both JSON operations like JSON serialization and deserialization(parsing) using the new System.JSON API.

Parsing JSON using Winter’12 API

The new JSON API is having decent documentation and most helpful post to start with is from Mr. Pat (aka MetaDaddy), you can read his post here.

Approach 1 – Parse JSON via Streaming Scary Tokens :((

Pat did a great job in describing how to parse JSON using the streaming token based approach. The streaming token is good from time/space complexity point of view, but if you have to parse a huge JSON structure, it become pain to write a parser using this, at least for me it was like $#$#%$^$. I was like understanding now what, is it START OBJECT, START ARRAY or FIELD… :((

Approach 2 – Using JSON.deserialize(…) method

Luckily, I found a very simple approach that can fit to “MOST” of the JSON parsing scenarios. I said most, because this approach will not work well, if your JSON structure is not naming the fields correctly.

This approach requires you to have well defined Apex classes mapped to each of the structure/entity represented in the JSON string. If your apex class exactly resembles the JSON structure you can parse the whole JSON in just one line of code. How ? we will see next.

Lets take the classical example of Departments, Employees and Addresses i.e. Department has many employees and employee can have multiple addresses.

Example JSON structure for one department :

{
   "name":"Engineering",
   "code":"ENGR",
   "employees":[
      {
         "name":"James Bond",
         "mobile":"123-456-7892",
         "isMale":true,
         "age":29,
         "addresses":[
            {
               "street":"900 Concar Drive",
               "state":"CA",
               "postalCode":94402,
               "isPrimary":true,
               "country":"USA",
               "city":"San mateo"
            },
            {
               "street":"800 Bridgepointe",
               "state":"CA",
               "postalCode":29230,
               "isPrimary":false,
               "country":"USA",
               "city":"Foster City"
            }
         ]
      },
      {
         "name":"Brad Pitt",
         "mobile":"987-456-7892",
         "isMale":true,
         "age":35,
         "addresses":[
            {
               "street":"900 Rough Drive",
               "state":"CA",
               "postalCode":13245,
               "isPrimary":true,
               "country":"USA",
               "city":"SFO"
            }
         ]
      }
   ]
}

Object model required for this JSON :

I have created following classes nested under a top level class called “JsonParserTests”.

public with sharing class JsonParserTests {
    
    class Department {
        public String name;
        public String code;
        public Employee[] employees;        
        
        public Department(String n, String c) {
            this.name = n;
            this.code = c;
            this.employees = new List<Employee>();
        }        
    } 
    
    class Employee {
        public String name;
        public Integer age;
        public String mobile;
        public boolean isMale;
        public Address[] addresses;
        
        public Employee(String n, Integer a, String m, boolean im) {
            this.name = n;
            this.age = a;
            this.mobile = m;
            this.isMale = im;
            this.addresses = new List<Address>();
        }
    }
    
    class Address {
        public String street;
        public String city;
        public String state;
        public Integer postalCode;
        public String country;
        public boolean isPrimary;
        
        public Address( String street, String city, String state, Integer postalCode, String country, boolean isPrimary) {
            this.street = street;
            this.city = city;
            this.state = state;
            this.postalCode = postalCode;
            this.country = country;
            this.isPrimary = isPrimary;
        }
    }
} 

 

Parsing the full JSON in one line of code.

Here is the method from System.JSON to do so :

// This department instance is having the full hierarchy
// of employees and addresses in it
Department d = (Department)JSON.deserialize(jsonToParse, JsonParserTests.Department.class);

The beauty of this JSON serialization is you get the full nested object hierarchy in JSON created in one go. Also, it just needs one script statement for the full parsing operation.

One might argue that the above nested classes more than one line of code. I would say its always good to visualize and work on Entities mapped to Classes/Types. Even if one is using streaming parser, they would have required similar nested classes by the end of parsing.

Approach 3 – Hybrid of Streaming Tokens and deserialize !

As I mentioned above in Approach 2 also, that all JSON strings can’t be deserialzed directly using “JSON.deserialize()” call. The JSON structure needs to be well formed with proper field names etc. The good news is one can use both streaming token parser and deserialization together i.e.

  • Parse partial JSON structure using streaming tokens

  • Parse part of JSON structure that can be mapped to an Apex class using “JSONParser.readValueAs()” API.

JSONParser.readValueAs() is almost similar to JSON.deserialize() call, only difference is it works on current token value, instead of taking the JSON string explicitly as param.

Here is an example of “JSONParser.readValueAs()” for the same department JSON structure, but using hybrid mode. Here we will only parse first employee object via JSONParser.readValueAs(..)

System.JSONParser parser = JSON.createParser(jsonToParse);
parser.nextToken(); // START OBJECT
parser.nextToken(); // name
parser.nextToken(); // engineering
parser.nextToken(); // employees
parser.nextToken(); // start array
parser.nextToken(); // start object
// only first employee object parsed.
Employee employee = (Employee)parser.readValueAs(JsonParserTests.Employee.class);  

Serializing JSON from Apex in one line of code !

Similar to the easy deserialization approach discussed above, one can serialize complete object graph too. For ex. the same above JSON structure for department can be created like this

Department engineering = new Department('Engineering', 'ENGR');

Employee e1 = new Employee('James Bond', 29,  '123-456-7892', true );
engineering.employees.add(e1);

e1.addresses.add(new Address('900 Concar Drive', 'San mateo', 'CA', 94402, 'USA', true));
e1.addresses.add(new Address('800 Bridgepointe', 'Foster City', 'CA', 29230, 'USA', false));

Employee e2 = new Employee('Brad Pitt', 35,  '987-456-7892', true );
engineering.employees.add(e2);
e2.addresses.add(new Address('900 Rough Drive', 'SFO', 'CA', 13245, 'USA', true));
// JSON string created and printed in debug logs
System.debug(JSON.serialize(engineering));

References

Your views and thoughts ?

Looking forward for your comments !!

Comments (10)

  • Anonymoussays:

    November 2, 2011 at 6:31 pm

    I created an account just to post this:I love you and I love this blog post. The thought of parsing my enormous JSON using streaming tokens was ruining my day. You just saved it.Gonna give this one a shot today. Thank you so much, please keep up the good work!Apex Dreamer

  • Anonymoussays:

    November 3, 2011 at 4:20 am

    Thanks @Apex-Dreamer, I feel really happy and motivated to see this appreciation. Many thanks !One Note : This deserialization is little buggy I believe, my code sometimes fails for the same JSON string, and it needs every json property to match with Apex class, if your JSON schema is not in hand, it would be risky deal.

  • Anonymoussays:

    November 7, 2011 at 6:58 pm

    Finally finished parsing my 20,000+ character JSON using your method #2 =)Thank you again for your very helpful post! Here are a few challenges that I ran into on the way:1. Some classes had over 32 fields. APEX allows only 32 parameters in a method, so the constructors wouldn't compile. I worked around this by splicing out a few of the unnecessary fields in the JSON2. One of the JSON object fields was an APEX reserved keyword “desc”. This was fixed by using String.replaceAll('”desc”', '”description”');Thank you again, now I can finally begin working on the front end =)Apex Dreamer

  • Anonymoussays:

    February 7, 2013 at 10:10 pm

    Hi Abhinav,I have a use case like below public class Sample{ public String a {get;set;} public Sample1 test {get;set;} public class Sample1{ // in response, for this class dont know about the fields }} How to handle such response containing list of object using JSON.deserialize /JSON.deserializeUntyped and map it to Apex class? Any pointer's on this would be highly appricate. Thanks in Advance.

  • Anonymoussays:

    February 8, 2013 at 4:11 am

    Vivek,I am not sure how you will make use of JSON structure whose fields are not known, might be you are storing it as it is. I think if structure of JSON is unkown then JSON.deserializeUntyped is better, but again this method needs to know which fields you want to pull. So not sure if it could help you.

Leave a Reply

%d bloggers like this: