October 12, 2011

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 !!