Getting Started with Apex JSON
Introduction to JSON
According to http://www.json.org:
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. … JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others.
This definition, in short, highlights the key aspects of JSON: lightweight, human-readable, language-independent format, with parsers available in many languages.
JSON is becoming a de-facto standard for many APIs: for example, Twitter, Facebook, and Google APIs heavily rely on JSON. It is primarily used for serializing and transmitting structured data over a network connection, typically between a server and web application. In this realm JSON challenges XML. While there have been multiple debates on “JSON vs. XML” topic, it is agreed that JSON has the following key advantages over XML:
- JSON has a smaller footprint than XML, which means it can be transmitted and parsed faster than XML.
- JSON is simpler than XML because it has a smaller grammar and maps better to the data structures of the modern programming languages.
We should mention that XML has its advantages as well, such as XML schema for instance; however, a full comparison of XML vs. JSON is out of the scope of the current article.
Getting Started
Now that you have a general understanding of JSON, the following sections introduce you to the key Apex classes related to JSON.
System.JSON
The System.JSON class provides methods for serialization and deserialization. For instance, the code snippet below serializes a list of Account objects into a JSON string:
List<Account> accounts = [SELECT id, name FROM Account LIMIT 2];
String accountsJSON = JSON.serializePretty(accounts);
Simply calling the serialize() method does the trick. The code above produces the JSON output similar to the following:
{
"attributes" : {
"type" : "Account",
"url" : "/services/data/v24.0/sobjects/Account/001d000000Ard7uAAB"
},
"Id" : "001d000000Ard7uAAB",
"Name" : "United Oil & Gas, Singapore"
}, {
"attributes" : {
"type" : "Account",
"url" : "/services/data/v24.0/sobjects/Account/001d000000Ard7vAAB"
},
"Id" : "001d000000Ard7vAAB",
"Name" : "Edge Communications"
}
Deserialization of the accountsJSON string can easily be done using JSON.deserialize method:
List<Account> accountsDeserialized = (List<Account>) JSON.deserialize(accountsJSON, List<Account>.class);
You can handle Apex objects similarly to sObjects with the System.JSON class. For instance, the GoogleCalendar class from the demo application you'll see a bit later ...
public class GoogleCalendar {
public String id;
public String kind;
public String etag;
public String summary;
public String description;
public String location;
public String timeZone;
public String summaryOverride;
public String colorId;
public Boolean hidden;
public Boolean selected;
public String accessRole;
public List<GoogleReminderOverride> defaultReminders;
...
}
... is serialized and deserialized just like an sObject, except now you control the JSON structure through the Apex class:
GoogleCalendar gCalendar = new GoogleCalendar();
//code to populate fields for the gCalendar is skipped
String gCalJSON = JSON.serialize(gCalendar);
GoogleCalendar gCalendarDeserialized = (GoogleCalendar) JSON.deserialize(gCalJSON, GoogleCalendar.class);
Using the System.JSON class methods significantly reduces the number of script statements required to process JSON content and eliminates the need to dynamically inspect the object schema to figure out what fields exist. It is an ideal choice for scenarios that involve working with structured data such as Apex objects.
System.JSONGenerator
The System.JSONGenerator class contains methods used to serialize Apex objects, and sObjects for that matter, into JSON content using the standard JSON encoding. The key difference between System.JSONGenerator and System.JSON’s serialize() method is that the former provides a manual control over the serialization process. Below is a simple example that creates basic JSON content after querying for contacts:
List<Contact> contacts = [SELECT Id, Name FROM Contact LIMIT 10];
JSONGenerator generator = JSON.createGenerator(true); //instantiation of the generator
generator.writeStartObject(); // Writes the starting marker of a JSON object '{'
generator.writeNumberField('count', contacts.size()); //Writes the # of contacts
generator.writeEndObject(); //Writes the ending marker of a JSON object '}'
String jsonString = generator.getAsString();
The code above produces a simple JSON string similar to the following:
{
"count" : 10
}
Now take a look at a more sophisticated example: serialization of a GoogleCalendarEvent Apex object used in the upcoming demo application. The structure of the JSON Google Events Resource is the following:
{
"id": string,
"htmlLink": string,
"created": datetime,
"summary": string,
"description": string,
"location": string,
"start": {
"date": date,
"dateTime": datetime,
"timeZone": string
},
"end": {
"date": date,
"dateTime": datetime,
"timeZone": string
},
"sequence": integer,
"attendees": [
{
"email": string,
"displayName": string,
"organizer": boolean,
"self": boolean,
"resource": boolean,
"optional": boolean,
"responseStatus": string,
"comment": string,
"additionalGuests": integer
}
],
"reminders": {
"useDefault": boolean,
"overrides": [
{
"method": string,
"minutes": integer
}
]
}
}
The JSON structure above maps to the GoogleCalendarEvent class:
public class GoogleCalendarEvent {
public String id;
public String htmlLink;
public DateTime created;
public String summary;
public String description;
public String location;
public Integer sequence;
public GoogleEventTime start;
public GoogleEventTime gEnd;
public List<GoogleEventAttendee> attendees;
public GoogleReminder reminders;
...
}
The calendar event JSON structure contains multiple data types, nested objects, and arrays — the GoogleCalendarEvent class reflects these and requires the use of various methods of the JSONGenerator class when creating the JSON content. In addition, some JSON structure’s properties, such as end, date, and dateTime are reserved Apex keywords and hence were renamed to gEnd, gDate, and gDateTime, respectively, for the GoogleCalendarEvent class.
For clarity, here's a walk through of the serialization process in steps:
//instantiate the generator
JSONGenerator gen = JSON.createGenerator(true);
gen.writeStartObject();
//this corresponds to an instance of the GoogleCalendarEvent class
gen.writeStringField('summary', this.summary);
gen.writeStringField('location', this.location);
gen.writeFieldName('start');
gen.writeStartObject();
gen.writeObjectField('dateTime', this.start.gDatetime);
gen.writeEndObject();
gen.writeFieldName('end');
gen.writeStartObject();
//for demo pusposes writeDateTimeField() is used instead of writeObjectField()
gen.writeDateTimeField('dateTime', this.gEnd.gDatetime);
gen.writeEndObject();
//serialize reminders
gen.writeFieldName('reminders');
//writeObject() does the trick automatically since Apex object field names are the same as JSON field names
gen.writeObject(this.reminders);
...
The code above produces a JSON string analogous to the following:
{
"summary": "summary_value",
"location": "location_value",
"start": {
"dateTime": "2012-02-15T18:03:32-08:00"
},
"end": {
"dateTime": "2012-02-15T19:03:32-08:00"
},
"reminders": {
"useDefault": false,
"overrides": [
{
"method": "email",
"minutes": 1
},
{
"method": "email",
"minutes": 2
}
]
}
...
writeStringField() writes a text value while writeDateTimeField() writes a dateTime. Notice how the writeObject() method serializes the entire reminders object including the overrides array — this is a handy tool that makes the code efficient. Now continue with the serialization of the array of event attendees:
...
gen.writeFieldName('attendees');
gen.writeStartArray();
//for each attendee create a JSON object
for(GoogleEventAttendee gEventAttendee: this.attendees){
gen.writeStartObject();
gen.writeStringField('email', gEventAttendee.email);
gen.writeBooleanField('optional', gEventAttendee.optional);
gen.writeNumberField('additionalGuests', gEventAttendee.additionalGuests);
gen.writeEndObject();
}
gen.writeEndArray();
//end of the parent JSON object
gen.writeEndObject();
String jsonString = gen.getAsString();
For demo purposes, instead of using the writeObject() method, the code manually constructs the attendees array, which results in the following JSON structure:
...
"attendees": [
{
"email": "aaa@qq.com",
"optional": true,
"responseStatus": "needsAction"
},
{
"email": "aaa@qq.com",
"optional": true,
"responseStatus": "needsAction"
}
]
System.JSONParser
In contrast to System.JSONGenerator, System.JSONParser does the opposite — it provides methods for parsing JSON content. Generally, JSONParser is useful for grabbing specific pieces of data without the need of a structure such as an Apex class. For example, the code snippet below, taken from the authentication related code of the demo app, grabs access token and the expires_in parameter from the JSON response received from the authentication service:
//resp is a JSON string
JSONParser parser = JSON.createParser(resp);
while (parser.nextToken() != null) {
if ((parser.getCurrentToken() == JSONToken.FIELD_NAME)){
String fieldName = parser.getText();
parser.nextToken();
if(fieldName == 'access_token') {
accesstoken = parser.getText();
} else if(fieldName == 'expires_in'){
expiresIn = parser.getIntegerValue();
}
}
}
Depending on the JSON content you need to parse, it may make sense to break the while loop after your code obtains all the necessary information and thus avoid unnecessary processing. The System.JSONToken is an enumerator that provides values such as FIELD_NAME, START_OBJECT, END_OBJECT and others that inform you of the type of token currently being parsed. Now take a look at how to construct the GoogleCalendarEvent object based on a parsed JSON string. For better readability, we'll break down parsing code into several pieces:
//this – is an instance of GoogleCalendarEvent class
//instantiate the parser
JSONParser parser = JSON.createParser(jsonString);
while (parser.nextToken() != null) {
//if current token is a field name
if (parser.getCurrentToken() == JSONToken.FIELD_NAME){
String fieldName = parser.getText();
System.debug('fieldName: ' + fieldName);
//move to token after the field name
parser.nextToken();
if(fieldName == 'id'){
this.id = parser.getText();
}
else if(fieldName == 'htmlLink'){
this.htmlLink = parser.getText();
}
...
The code above executes a while loop that walks through the entire JSON string using the parser.nextToken() method and parses simple field text values such as id or htmlLink using the parser.getText() method. Now take a look at how to parse the start object (the code below is inside the while loop from the previous snippet):
...
else if(fieldName == 'start'){ //start is a GoogleEventTime object
if(parser.getCurrentToken() == JSONToken.START_OBJECT){
while(parser.nextToken() != null){
if(parser.getCurrentToken() == JSONToken.FIELD_NAME){
if(parser.getText() == 'dateTime'){
parser.nextToken();
this.start.gDateTime = parser.getDateTimeValue();
break;
}
}
}
}
}
...
You use JSONToken.START_OBJECT enum to determine the beginning of the start object, and then using the inner while loop, iterate through its content to grab the this.start.gDateTime value using the parser.getDateTimeValue() method. Notice the break statement to stop the inner loop — without it, the inner loop would continue processing the remaining JSON, which is the responsibility of the outer while loop.
Next, parse the attendees array using the code below:
...
else if(fieldName == 'attendees'){
if(parser.getCurrentToken() == JSONToken.START_ARRAY){
while(parser.nextToken() != null){
if(parser.getCurrentToken() == JSONToken.START_OBJECT){
//read GoogleEventAttendee object
GoogleEventAttendee gEventAttendee = (GoogleEventAttendee) parser.readValueAs(GoogleEventAttendee.class);
this.attendees.add(gEventAttendee);
}
else if(parser.getCurrentToken() == JSONToken.END_ARRAY){
break;
}
}
}
}
...
After detecting the attendees array, the code uses the readValueAs() method to deserialize the JSON structure into the GoogleEventAttendee object automatically — in other words, without parsing the fields separately.
Demo App Walk-Through
Now that you know the basics of JSON handling, try walking through a demo application that integrates with the Google Calendar APIs and uses JSON format to send and receive calendar information. Before you can use the demo app, you have to go through the prerequisite steps below.
Prerequisites
Before you can use the demo app, you need to:
- Download the unmanaged package into your developer org.
- Go through the Google API configuration steps.
- Authenticate with the API from using Google Apps custom object record.
Here's how.
Step 1: Log In to a Developer Edition Org
Log in to a Developer Edition org. If you don't have one, create a new one for free.
Step 2: Install the Demo App Package
In a new browser tab, launch the installation of the demo app package into your Developer org using the URL in the link. Once the installation starts, click Continue at the Package Installation Details page, then approve the third-party website access and click Continue.
Click Next at the Approve Package API Access page, then select Grant access to all users and click Next at the Choose Security Level page.
Click Install at the Install Package page, after which the package has been installed into your org.
Step 3: Create a Google APIs Project
In a web browser, navigate to the Google APIs Console, log in using a Google/Gmail account that you use to manage your calendar, and then create a project.
On the Services tab, turn on the Calendar API and accept Google’s terms of service.
Step 4: Enable the OAuth 2.0 Authentication for Your Google Calendar
On the API Access tab in the Google APIs Console, click Create an OAuth 2.0 client ID ....
Enter a Product Name such as GCalendar
, then click Next.
On the next screen, click Web Application and enter c.your_salesforce_instance.visual.force.com/apex/GoogleLogin
into the Your site or hostname field. Make sure you enter the correct Salesforce.com instance name, which can be obtained from the address bar of your browser:
As soon as you move the cursor focus out of the site or hostname field, the page updates so that the Redirect URI at the bottom of the dialog turns into https://c.your_salesforce_instance.visual.force.com/apex/GoogleLogin.
Finally, click Create client ID to generate the Client ID and Client Secret.
Step 5: Configure Authentication
Log in or return to your Developer Edition org and launch the JSON App from the Force.com App Menu at the top right corner.
Click the Google Apps tab, click New to create a new record, then populate the following fields:
-
GoogleApp Name: enter
GoogleApp
- Client ID: copy/paste the Client ID from the Google APIs Console
- Client Secret: copy/paste the Client Secret from the Google APIs Console
-
Scope: enter
https://www.googleapis.com/auth/calendar
, which provides read-write access to Calendars, Calendar Events, and Calendar ACLs
Click Save.
Now that you have all the necessary information on the Google App record, you can authenticate against the Google APIs. First, disable Development Mode for your account, if necessary.
- Click Setup → My Personal Information → Personal Information → Edit.
- Disable Development Mode, if necessary.
- Click Save.
To authenticate:
- Load the JSON App.
- Click Google Apps → Google App → Authenticate.
Google displays a page asking if you are OK with providing the required permissions to the application. Click Allow access to grant the access.
If everything went through fine, your GoogleApp record now has populated Access Token, Expires In, and Code fields. The access token typically is good for one hour.
Creating Calendars and Events
Assuming that the authentication went fine, now try creating a new Google calendar. Click the Create Calendar tab, enter some values for the calendar, and click Create Calendar.
When you press the Create Calendar button, a few actions take place:
- The information you entered gets assigned to a GoogleCalendar object.
- The object gets serialized into a JSON string using System.JSON.
- The JSON string gets passed to the Google Calendar API via an HTTP callout.
- The HTTP request header’s Content-Type property is set to application/json.
- As a result, Google creates a new calendar and sends back information such as id and etag as a JSON string.
- The app deserializes the JSON string into another GoogleCalendarObject and displays it in the Calendar Output section.
To verify the new calendar, navigate to your Gmail calendar. If everything went as expected, you should see a new calendar entry similar to the following:
Now, create a new event for the calendar you just created. To do so, click the Create Calendar Event tab, select the newly created calendar’s name from the drop-down, enter sample values and click Create Calendar Event:
A serialized (using JSONGenerator) GoogleCalendarEvent object gets passed to the API. Eventually, the Calendar Event Output section displays the deserialized (using JSONParser) GoogleCalendarEvent object obtained from the API call response. To verify if the calendar event got created, navigate to the Gmail calendar page.
Summary
The Apex JSON classes provide easy-to-use means of serializing/deserializing Apex objects into/from JSON content. Native JSON implementation empowers you with ability to build robust applications that integrate with third-party systems, various JavaScript libraries, or use HTML5. This article explained the basics about handling JSON content in Apex. It provided a walk-through of the Force.com demo application that integrates with the Google APIs and uses JSON as the data-interchange format. The code samples provided in the article demonstrated how different serialization and deserialization options could be implemented in Apex.
上一篇: SOCKET