Getting Started with Apex JSON

来源:互联网 发布:演唱会购票软件 编辑:程序博客网 时间:2024/05/15 01:02

https://developer.salesforce.com/page/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:

1List<Account> accounts = [SELECT id, name FROM Account LIMIT 2];
2String accountsJSON = JSON.serializePretty(accounts);

Simply calling the serialize() method does the trick. The code above produces the JSON output similar to the following:

01{
02  "attributes" : {
03    "type" "Account",
04    "url" "/services/data/v24.0/sobjects/Account/001d000000Ard7uAAB"
05  },
06  "Id" "001d000000Ard7uAAB",
07  "Name" "United Oil & Gas, Singapore"
08}, {
09  "attributes" : {
10    "type" "Account",
11    "url" "/services/data/v24.0/sobjects/Account/001d000000Ard7vAAB"
12  },
13  "Id" "001d000000Ard7vAAB",
14  "Name" "Edge Communications"
15}

Deserialization of the accountsJSON string can easily be done using JSON.deserialize method:

1List<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 ...

01public class GoogleCalendar {
02    public String id;
03    public String kind;
04    public String etag;
05    public String summary;
06    public String description;
07    public String location;
08    public String timeZone;
09    public String summaryOverride;
10    public String colorId;
11    public Boolean hidden;
12    public Boolean selected;
13    public String accessRole;
14    public List<GoogleReminderOverride> defaultReminders;
15    ...
16}

... is serialized and deserialized just like an sObject, except now you control the JSON structure through the Apex class:

1GoogleCalendar gCalendar = new GoogleCalendar();
2//code to populate fields for the gCalendar is skipped
3String gCalJSON = JSON.serialize(gCalendar);
4GoogleCalendar 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:

1List<Contact> contacts = [SELECT Id, Name FROM Contact LIMIT 10];
2JSONGenerator generator = JSON.createGenerator(true);   //instantiation of the generator
3generator.writeStartObject();               // Writes the starting marker of a JSON object '{'
4generator.writeNumberField('count', contacts.size());   //Writes the # of contacts
5generator.writeEndObject();                 //Writes the ending marker of a JSON object '}'
6String jsonString = generator.getAsString();

The code above produces a simple JSON string similar to the following:

1{
2  "count" 10
3}

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:

01{
02  "id": string,
03  "htmlLink": string,
04  "created": datetime,
05  "summary": string,
06  "description": string,
07  "location": string,
08  "start": {
09    "date": date,
10    "dateTime": datetime,
11    "timeZone": string
12  },
13  "end": {
14    "date": date,
15    "dateTime": datetime,
16    "timeZone": string
17  }, 
18  "sequence": integer,
19  "attendees": [
20    {
21      "email": string,
22      "displayName": string,
23      "organizer"boolean,
24      "self"boolean,
25      "resource"boolean,
26      "optional"boolean,
27      "responseStatus": string,
28      "comment": string,
29      "additionalGuests": integer
30    }
31  ], 
32  "reminders": {
33    "useDefault"boolean,
34    "overrides": [
35      {
36        "method": string,
37        "minutes": integer
38      }
39    ]
40  }
41}

The JSON structure above maps to the GoogleCalendarEvent class:

01public class GoogleCalendarEvent {
02        public String id;
03    public String htmlLink;
04    public DateTime created;
05    public String summary;
06    public String description;
07    public String location;
08    public Integer sequence;
09    public GoogleEventTime start;
10    public GoogleEventTime gEnd;
11    public List<GoogleEventAttendee> attendees;
12    public GoogleReminder reminders;
13...
14}

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 enddate, 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:

01//instantiate the generator
02JSONGenerator gen = JSON.createGenerator(true);
03gen.writeStartObject();
04//this corresponds to an instance of the GoogleCalendarEvent class
05gen.writeStringField('summary'this.summary);
06gen.writeStringField('location'this.location);
07gen.writeFieldName('start');
08gen.writeStartObject();       
09gen.writeObjectField('dateTime'this.start.gDatetime);
10gen.writeEndObject();
11gen.writeFieldName('end');
12gen.writeStartObject();       
13//for demo pusposes writeDateTimeField() is used instead of writeObjectField()
14gen.writeDateTimeField('dateTime'this.gEnd.gDatetime);           
15gen.writeEndObject();
16//serialize reminders
17gen.writeFieldName('reminders');
18//writeObject() does the trick automatically since Apex object field names are the same as JSON field names
19gen.writeObject(this.reminders);
20...

The code above produces a JSON string analogous to the following:

01{
02“summary”: “summary_value”,
03“location”: “location_value”,
04"start": {
05"dateTime""2012-02-15T18:03:32-08:00"
06},
07"end": {
08"dateTime""2012-02-15T19:03:32-08:00"
09},
10"reminders": {
11    "useDefault"false,
12    "overrides": [
13        {
14        "method""email",
15        "minutes"1
16        },
17        {
18        "method""email",
19            "minutes"2
20        }
21    ]
22}
23...

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:

01...
02gen.writeFieldName('attendees');
03gen.writeStartArray();
04//for each attendee create a JSON object
05for(GoogleEventAttendee gEventAttendee: this.attendees){
06    gen.writeStartObject();
07    gen.writeStringField('email', gEventAttendee.email);           
08    gen.writeBooleanField('optional', gEventAttendee.optional);
09    gen.writeNumberField('additionalGuests', gEventAttendee.additionalGuests);
10    gen.writeEndObject();
11}
12gen.writeEndArray();
13//end of the parent JSON object
14gen.writeEndObject();
15String 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:

01...
02"attendees": [
03  {
04   "email""testemail-1@test.com",
05   "optional"true,
06   "responseStatus""needsAction"
07  },
08  {
09   "email""testemail-0@test.com",
10   "optional"true,
11   "responseStatus""needsAction"
12  }
13]

System.JSONParser

In contrast to System.JSONGeneratorSystem.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 theexpires_in parameter from the JSON response received from the authentication service:

01//resp is a JSON string
02JSONParser parser = JSON.createParser(resp);
03while (parser.nextToken() != null) {
04    if ((parser.getCurrentToken() == JSONToken.FIELD_NAME)){
05        String fieldName = parser.getText();
06        parser.nextToken();
07        if(fieldName ='access_token') {
08            accesstoken = parser.getText();
09        else if(fieldName ='expires_in'){
10            expiresIn = parser.getIntegerValue();
11        }
12}
13}

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_NAMESTART_OBJECTEND_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:

01//this – is an instance of GoogleCalendarEvent class
02//instantiate the parser
03JSONParser parser = JSON.createParser(jsonString);
04while (parser.nextToken() != null) {
05    //if current token is a field name
06    if (parser.getCurrentToken() == JSONToken.FIELD_NAME){
07        String fieldName = parser.getText();
08        System.debug('fieldName: ' + fieldName);
09        //move to token after the field name
10        parser.nextToken();
11        if(fieldName ='id'){
12            this.id = parser.getText();
13        }
14        else if(fieldName ='htmlLink'){
15            this.htmlLink = parser.getText();
16        }
17    ...

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):

01...
02  else if(fieldName ='start'){    //start is a GoogleEventTime object
03    if(parser.getCurrentToken() == JSONToken.START_OBJECT){
04        while(parser.nextToken() != null){
05            if(parser.getCurrentToken() == JSONToken.FIELD_NAME){
06                if(parser.getText() ='dateTime'){
07                    parser.nextToken();
08                    this.start.gDateTime = parser.getDateTimeValue();
09                    break
10                }
11            }
12        }
13    }                  
14  }
15...

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:

01...
02else if(fieldName ='attendees'){
03if(parser.getCurrentToken() == JSONToken.START_ARRAY){
04    while(parser.nextToken() != null){
05        if(parser.getCurrentToken() == JSONToken.START_OBJECT){
06            //read GoogleEventAttendee object
07GoogleEventAttendee gEventAttendee = (GoogleEventAttendee) parser.readValueAs(GoogleEventAttendee.class);
08            this.attendees.add(gEventAttendee);
09        }
10        else if(parser.getCurrentToken() == JSONToken.END_ARRAY){
11            break
12        }
13    }
14}
15}
16...

After detecting the attendees array, the code uses the readValueAs() method to deserialize the JSON structure into theGoogleEventAttendee 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 clickContinue.

Gs-apex-json-13.png

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.

Gs-apex-json-15.png

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.

Gs-apex-json-1.png

On the Services tab, turn on the Calendar API and accept Google’s terms of service.

Gs-apex-json-2.png

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

Gs-apex-json-3.png

Enter a Product Name such as GCalendar, then click Next.

Gs-apex-json-4.png

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:

Gs-apex-json-17.png

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.

Gs-apex-json-5.png

Finally, click Create client ID to generate the Client ID and Client Secret.

Gs-apex-json-6.png

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.

Gs-apex-json-18.png

Click the Google Apps tab, click New to create a new record, then populate the following fields:

  1. GoogleApp Name: enter GoogleApp
  2. Client ID: copy/paste the Client ID from the Google APIs Console
  3. Client Secret: copy/paste the Client Secret from the Google APIs Console
  4. Scope: enter https://www.googleapis.com/auth/calendar, which provides read-write access to Calendars, Calendar Events, and Calendar ACLs

Click Save.

Gs-apex-json-9.png

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.

  1. Click Setup → My Personal Information → Personal Information → Edit.
  2. Disable Development Mode, if necessary.
  3. Click Save.

To authenticate:

  1. Load the JSON App.
  2. Click Google Apps → Google App → Authenticate.

Gs-apex-json-10.png

Google displays a page asking if you are OK with providing the required permissions to the application. Click Allow access to grant the access.

Gs-apex-json-11.png

If everything went through fine, your GoogleApp record now has populated Access TokenExpires 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.

Gs-apex-json-19.png

When you press the Create Calendar button, a few actions take place:

  1. The information you entered gets assigned to a GoogleCalendar object.
  2. The object gets serialized into a JSON string using System.JSON.
  3. 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.
  4. As a result, Google creates a new calendar and sends back information such as id and etag as a JSON string.
  5. 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:

Gs-apex-json-20.png

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:

Gs-apex-json-21.png

A serialized (using JSONGeneratorGoogleCalendarEvent object gets passed to the API. Eventually, the Calendar Event Output section displays the deserialized (using JSONParserGoogleCalendarEvent object obtained from the API call response. To verify if the calendar event got created, navigate to the Gmail calendar page.

Gs-apex-json-22.png

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.

Related Links

  1. Google Calendar APIs reference
  2. JSON documentation: http://json.org/
  3. Apex JSON documentation
  4. Unmanaged package with the demo app

About the Author

Shamil Arsunukayev is a Technical Architect at Comity Designs Inc. His primary focus is development of cloud applications, specifically Force.com applications and customizations. Shamil often participates in various Force.com developer challenges and hackathons. He can be found on @shamiltech and his blog assertequals.com


0 0
原创粉丝点击