JSON Deserialization in Salesforce

JSON Logo

I have been several posts recently on the Developer Boards around JSON deserialization and some weird and convoluted ways to convert it into something that is useful for Salesforce.  Let’s talk about what I have found is the cleanest way to handle JSON deserialization.

JSON Payload

Let’s take a look at our JSON payload.  I am taking the payload from the docsample Heroku app since it’s an easy way to get consistent data from a webservice.

{
  "invoiceList": [
    {
      "totalPrice": 5.5,
      "statementDate": "2011-10-04T16:58:54.858Z",
      "lineItems": [
        {
          "UnitPrice": 1,
          "Quantity": 5,
          "ProductName": "Pencil"
        },
        {
          "UnitPrice": 0.5,
          "Quantity": 1,
          "ProductName": "Eraser"
        }
      ],
      "invoiceNumber": 1
    },
    {
      "totalPrice": 11.5,
      "statementDate": "2011-10-04T16:58:54.858Z",
      "lineItems": [
        {
          "UnitPrice": 6,
          "Quantity": 1,
          "ProductName": "Notebook"
        },
        {
          "UnitPrice": 2.5,
          "Quantity": 1,
          "ProductName": "Ruler"
        },
        {
          "UnitPrice": 1.5,
          "Quantity": 2,
          "ProductName": "Pen"
        }
      ],
      "invoiceNumber": 2
    }
  ]
}

So we can see here that the data provided is an invoice list and each invoice contains data and line items for that invoice.

JSON Deserialization

Data Structure

Now we need to create a data structure to hold our the JSON we deserialize

public class InvoiceWrapper {
    public class LineItem {
        public Double unitPrice {get; set;}
        public Double quantity {get; set;}
        public String productName {get; set;}

        public Double getLineItemTotal() {
            return this.unitPrice * this.quantity;
        }
    }

    public class Invoice {
        public Double totalPrice {get; set;}
        public DateTime statementDate {get; set;}
        public String contactnumber {get; set;}
        public List<LineItem> lineItems {get; set;}
        public Integer invoiceNumber {get; set;}
    }

    public List<Invoice> invoiceList {get; set;}
}

This wrapper class now contains our two sub-classes (LineItem and Invoice) as well as our variable for our invoice list.  The nice thing about doing it as a class is we can add helper methods to also manipulate data.  There is a getLineItemTotal method that we can use in our display.

Data Parsing

Now we need to pull the data from the endpoint and using JSON deserialization push it into our data structure.

public class JSONDeserialize {
    public InvoiceWrapper wrapper {
        get;
        set;
    }

    public void deserialize() {
        Http h = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndPoint('https://docsample.herokuapp.com/jsonSample');
        request.setHeader('Content-type', 'application/json');
        request.setMethod('GET');

        HttpResponse response = h.send(request);

        wrapper = (InvoiceWrapper) JSON.deserializeStrict(response.getBody(), InvoiceWrapper.class);
    }
}

If your JSON data is going to change (or could change) you can use deserialize instead of deserializeStrict to make it not explode when the JSON deserialization happens.

Data Display

Now that we have a way to get the data in a meaningful structure, let’s display it on a Visualforce page

<apex:page controller="JSONDeserialize">
    <apex:form >
        <apex:pageBlock title="JSON Deserialize Response">
            <apex:pageBlockButtons >
                <apex:commandButton value="submit" action="{!deserialize}" reRender="invoiceBlock"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection id="invoiceBlock" columns="1">
                <apex:repeat value="{!wrapper.invoiceList}" var="invoice">
                    <apex:pageBlockSection columns="2">
                        <apex:facet name="header">Invoice {!invoice.invoiceNumber}</apex:facet>
                        <apex:pageBlockSectionItem >
                            <apex:outputLabel value="Total Price" for="totalPrice" />
                            <apex:outputText value="{!invoice.totalPrice}" id="totalPrice" />
                        </apex:pageBlockSectionItem>
                        <apex:pageBlockSectionItem >
                            <apex:outputLabel value="Statement Date" for="statementDate" />
                            <apex:outputText value="{!invoice.statementDate}" id="statementDate" />
                        </apex:pageBlockSectionItem>
                    </apex:pageBlockSection>
                    <apex:pageBlockSection columns="1">
                        <apex:facet name="header">Invoice {!invoice.invoiceNumber} Items</apex:facet>
                        <apex:pageBlockTable value="{!invoice.lineItems}" var="item" id="lineItems">
                            <apex:column value="{!item.productName}" headerValue="Product Name" />
                            <apex:column value="{!item.quantity}" headerValue="Quantity" />
                            <apex:column value="{!item.unitPrice}" headerValue="Unit Price" />
                            <apex:column value="{!item.lineItemTotal}" headerValue="Total" />
                        </apex:pageBlockTable>
                    </apex:pageBlockSection>
                </apex:repeat>
            </apex:pageBlockSection>
        </apex:pageBlock> 
    </apex:form>
</apex:page>

Now when we click the submit button we can see the data coming in and when it’s pressed we deserialize the data and reRender the section

JSON Deserialization in action

This entry was posted in Development, Salesforce and tagged , , . Bookmark the permalink.
  • JimBTek

    This article makes it really straightforward to see how to do this step by step. Would be interesting to have a second piece that includes storing the data in a Salesforce custom object, and perhaps even different ways of getting this data to sync 1-way or 2-ways. Thanks for the great article!

  • You mean extending this so that the data in InvoiceWrapper is stored into an Invoice__c object? Can you expand upon what you mean by “1-way or 2-ways” sync?

  • JimBTek

    Yeah storing the data in a custom object.

    1-way: Sync data from JSON to Salesforce ongoing (think syncing in invoices for read only purpose in Salesforce

    2-way: User edits the Invoice record in Salesforce and it pushes back through JSON to Heroku, etc.

    Currently on a project that does 2-way syncing of invoices between Salesforce and Quickbooks Online, but we use a 3rd party syncing tool that uses XML to map across the two. It would be interesting to see more of how to roll your own connection.

  • Jerry Clifft

    I would really like to see this write the data to a custom object/fields instead of displaying to visualforce.

  • I’ll try to write something up for you, but there are two ways to do this. You can do the object way above and add a method that converts the data to an sObject or if the incoming JSON is in the same format as your sObject you can just deserialize it directly using sObject.class

  • Hi, I just tried to use your method in my org but struggling to get the results.

    https://developer.salesforce.com/forums/ForumsMain?id=9060G000000UZKvQAO

  • I’ve replied back to your post

  • Hara Prasad Sahoo

    Just a small correction, I had to change the datatype of Quantity field to Double.
    public class lineitem{
    public Double unitprice{get;set;}
    public Double quantity{get;set;}
    public String productname{get;set;}
    }

    rest all is good, works like a charm

  • It was already set as a double in the on the post. Did I miss a place?

  • Hara Prasad Sahoo

    my bad, i might have missed it:)

  • Hara Prasad Sahoo

    I was working on a similar task.
    I have tried to use a wrapper class to deserialise the json and put it into a wrapper class, which I have declared earlier.
    I am getting an error at this point below:
    jsonOutput results = (jsonOutput) JSON.deserialize(response.getBody(), jsonOutput.class);

    Error is :19:26:06:943 FATAL_ERROR System.JSONException: Malformed JSON: Expected ‘{‘ at the beginning of object

    please advise if I am missing anything here.

    Class:
    public with sharing class WarehouseCalloutService {

    private static final String WAREHOUSE_URL = ‘https://th-superbadge-apex.herokuapp.com/equipment’;

    // wrapper class
    public class Jsonwrapper{
    public String x_id;
    public Boolean replacement;
    public Integer quantity;
    public String name;
    public Integer maintenanceperiod;
    public Integer lifespan;
    public Integer cost;
    public String sku;
    }

    public class JsonOutput{
    public Jsonwrapper wrapper;
    }
    @future(callout=true)
    public static void runWarehouseEquipmentSync(){
    Http h = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndPoint(WAREHOUSE_URL);
    request.setHeader(‘Content-type’, ‘application/json’);
    request.setMethod(‘GET’);

    HttpResponse response = h.send(request);

    if (response.getStatusCode() == 200) {

    jsonOutput results = (jsonOutput) JSON.deserialize(response.getBody(), jsonOutput.class);
    system.debug(‘##’ + results);

    }

    }
    }

  • The problem you are having is because you’re not parsing the JSON in the way that it is formatted. You need to parse a list of “Jsonwrapper” instead. Below I’ve renamed your “Jsonwrapper” class to be Equipment (to better line up with what it is)


    public class Equipment {
    /* Elements go here */
    }

    /* Rest of http stuff goes here */

    List = (List) JSON.deserialize(response.getBody(), List.class);

  • Sagar Panwar

    Hey! Patrick Connelly, Thank you my friend. The example is great. It worked for me. I am new to salesforce and I have a question.
    Can we use the same process to fetch data from woo commerce to our salesforce. I don’t want to use external plugins.
    https://uploads.disquscdn.com/images/ffdfa5cfb94d4aa1a8cf36f7443bb5e2051aa5fa55585d2c5af3a42190e57823.png

  • Yes, you should be able to do this with any REST endpoint. I recommend going over the Apex Integration Services (specifically the Apex REST Callouts module) [1]. Be warned that depending on the number of licenses you have, you want to monitor your API limits. You may also want to look at implementing platform cache [2] to reduce the number of API calls you make, if your data doesn’t change super frequently.

    [1] https://trailhead.salesforce.com/modules/apex_integration_services
    [2] https://trailhead.salesforce.com/modules/platform_cache

  • Sagar Panwar

    [
    {
    “id”: 38,
    “parent_id”: 0,
    “number”: “38”,
    “order_key”: “wc_order_5b472b646db19”,
    “created_via”: “checkout”,
    “version”: “3.4.3”,
    “status”: “processing”,
    “currency”: “INR”,
    “date_created”: “2018-07-12T10:20:20”,
    “date_created_gmt”: “2018-07-12T10:20:20”,
    “date_modified”: “2018-07-12T10:20:20”,
    “date_modified_gmt”: “2018-07-12T10:20:20”,
    “discount_total”: “0.00”,
    ….

    My JSON is in this format. And I am getting the error
    Malformed JSON: Expected ‘{‘ at the beginning of object
    Error is in expression ‘{!deserialize}’ in component in page wooordersdatadisplay: Class.System.JSON.deserializeStrict: line 19, column 1
    Class.OrdersJSONDeserialize.deserialize: line 27, column 1

    My Wrapper class
    public class OrdersWrapper {
    public class Billing{
    public String first_name {get; set;}
    public String last_name {get; set;}
    public String company {get; set;}
    public String address_1 {get; set;}
    public String address_2 {get; set;}
    public String city {get; set;}
    public String state {get; set;}
    public String postcode{get; set;}
    public String country {get; set;}
    public String email {get; set;}
    public String phone {get; set;}
    }

    public class Shipping {
    public String first_name {get; set;}
    public String last_name {get; set;}
    public String company {get; set;}
    public String address_1 {get; set;}
    public String address_2 {get; set;}
    public String city {get; set;}
    public String state {get; set;}
    public String postcode{get; set;}
    public String country {get; set;}
    }

    public class LineItems{
    public Integer id {get; set;}
    public String name {get; set;}
    public Integer product_id{get; set;}
    public Integer variation_id {get; set;}
    public Integer quantity {get; set;}
    public Double tax_class {get; set;}
    public String subtotal {get; set;}
    public Double subtotal_tax{get; set;}
    public Double total{get; set;}
    public Double total_tax {get; set;}
    public List taxes {get; set;}
    public List metadata {get; set;}
    public String sku {get; set;}
    public Integer price {get; set;}
    }

    public class TaxLines{

    }

    public class ShippingLines{
    public Integer id {get; set;}
    public String method_title {get; set;}
    public String method_id {get; set;}
    public String instance_id {get; set;}
    public Double total {get; set;}
    public Double total_tax {get; set;}
    public List taxes {get; set;}
    public List metadata {get; set;}
    }

    public class Taxes{

    }
    public class MetaData{
    public Integer id {get; set;}
    public String key {get; set;}
    public String value {get; set;}
    }

    public class FeeLines{}
    public class CouponLines{}
    public class Refunds{}

    public class Links{
    public List self {get; set;}
    public List collection {get; set;}
    public List customer {get; set;}
    }
    public class Self{
    public String href {get; set;}
    }
    public class Collection{
    public String href {get; set;}
    }
    public class Customer {
    public String href {get; set;}
    }

    public class Orders{
    public Integer id {get; set;}
    public Integer parent_id {get; set;}
    public String numbero {get; set;}
    public String order_key {get; set;}
    public String created_via {get; set;}
    public String version {get; set;}
    public String status {get; set;}
    public String currency_o {get; set;}
    public DateTime date_created {get; set;}
    public DateTime date_created_gmt {get; set;}
    public DateTime date_modified {get; set;}
    public DateTime date_modified_gmt {get; set;}
    public Double discount_total {get; set;}
    public Double discount_tax {get; set;}
    public Double shipping_total {get; set;}
    public Double shipping_taxt {get; set;}
    public Double cart_tax {get; set;}
    public Double total {get; set;}
    public Double total_tax {get; set;}
    public String prices_include_tax {get; set;}
    public Integer customer_id{get; set;}
    public String customer_ip_address {get; set;}
    public String customer_user_agent {get; set;}
    public String customer_note {get; set;}
    public String payment_method {get; set;}
    public String payment_method_title {get; set;}
    public String transaction_id {get; set;}
    public String date_paid {get; set;}
    public String date_paid_gmt {get; set;}
    public String date_completed {get; set;}
    public String date_completed_gmt {get; set;}
    public String cart_hash {get; set;}

    public List billing {get; set;}
    public List lineitems {get; set;}
    public List metadata{get; set;}
    public List taxlines {get; set;}
    public List feelines {get; set;}
    public List coupons_lines {get; set;}
    public List links {get; set;}

    }

    public List orders {get; set;}
    }

    public class OrdersJSONDeserialize {
    public OrdersWrapper wrapper{
    get;
    set;
    }

    public void deserialize(){
    Http h = new Http();
    HttpRequest request = new HttpRequest();

    String endurl = ‘…’;
    request.setEndpoint(endurl);
    request.setHeader(‘Content-type’, ‘application/json’);
    request.setMethod(‘GET’);

    //System.debug(‘Before response — start — ‘);

    HttpResponse response = h.send(request);

    //String jsonString = ‘{“orders” : ‘+ response.getBody() +’}’;
    String jsonString = response.getBody();

    System.debug(‘url is: ‘+endurl);

    System.debug(‘After response –‘ + jsonString);

    wrapper = (OrdersWrapper) JSON.deserializeStrict(jsonString, OrdersWrapper.class);
    //List wrapper = (List)JSON.deserialize(response.getBody(), List.class);

    }
    }

  • The line you commented out is required. The JSON is not valid because it’s just an array. JSON has to start with { not [ which is why the line is ‘{“orders”: ‘ + response.getBody() + ‘}’ to make it valid JSON.

  • Sagar Panwar

    But I am getting this from woocommerce rest api

  • Again, this is because the data coming back from woocommerce isn’t technically valid JSON. All valid JSON has to be surrounded by {}. That’s why in the code we surround the body with an element to make it valid JSON. If you do what I suggest above, you won’t get the parsing error

  • nti

    Hi Pat ,
    I went through your code and on the basis of that I have written some code but its of no use.So if u get time to look into it please where is the problem in the code.

    I am not getting any error but when I run it anonymously and try to see debug statements its only showing null value to me.

    I have called news api but i can only see null results.

    I have all the payload needed classes inside newsapi class.All of them are inner classes of NewsApi class.

    Public class Newsapi
    {

    public NewsApi news
    {
    get;
    set;
    }
    public void ApexNes()
    {

    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint(‘https://newsapi.org/v2/top-headlines?sources=cnn&apiKey=a257ba6c725a4b8dad4923aa24c75b0e’);
    //request.setMethod(‘Get’);
    request.setMethod(‘GET’);
    request.setHeader(‘Content-Type’, ‘application/json;charset=UTF-8’);
    HttpResponse response = http.send(request);
    //system.debug(‘Response body has the following item’+response.getbody());

    news=(newsapi)json.deserialize(response.getBody(),newsapi.class);

    System.debug(‘The variable under has the following values’+news);

    //System.debug(‘The variable under has the following values’+news.status);

    }

    public class JSON2Apex {

    public String status {get;set;}
    public Integer totalResults {get;set;}
    public List articles {get;set;}

    }

    public class Articles {
    public Source source {get;set;}
    public Object author {get;set;}
    public String title {get;set;}
    public String description {get;set;}
    public String url {get;set;}
    public String urlToImage {get;set;}
    public String publishedAt {get;set;}
    public String content {get;set;}
    }

    public class Source
    {
    public String id {get;set;}
    public String name {get;set;}
    }

    }

    Thanks

  • The problem is you’re deserializing into the wrong class. You should instead have

    public JSON2Apex news { get; set; }

    news=(JSON2Apex)json.deserialize(response.getBody(), JSON2Apex.class);

    I’d recommend renaming that highest level class to something like Headlines