Example Using Generic (JSON) Resource Objects - BloomReach Experience - Open Source CMS

Example Using Generic (JSON) Resource Objects

Introduction

Resource is the primary object representation to solve all the complex integration problems in a generic way. So, the interface was designed to support all the different domain object representations from heterogeneous backends easily by providing generic APIs to retrieve properties and child objects of domain objects.

One typical Resource implementation nowadays is probably in JSON data format. CRISP provides a default implementation of Resource interface for JSON data with JacksonResource which uses Jackson library to parse JSON data.

Single Resource Object from a JSON Object

Let's see a simple JSON data representing a product data from a backend system:

{
  "SKU": "12345678901",
  "description": "MultiSync X123BT - 109.22 cm (43 \") , 1920 x 480, 16:4, 500 cd\/m\u00b2, 3000:1, 8 ms",
  "name": "CBA MultiSync X123BT",
  "extendedData": {
    "title": "CBA MultiSync X123BT",
    "type": "Link",
    "uri": "Awesome-HIC-Site\/-\/products\/12345678901",
    "description": "MultiSync X123BT - 109.22 cm (43 \") , 1920 x 480, 16:4, 500 cd\/m\u00b2, 3000:1, 8 ms"
  }
}

The example JSON data shown above can be mapped to a Resource object. For more details,

  • The top level JSON object is mapped to a Resource object.
  • Properties such as "SKU", "description" and "name" are treated as primitive properties of Resource object. And, JacksonResource will convert each value to a properly typed value (e.g, String, Number, etc.) automatically by using Jackson library.
  • Child JSON object such as "extendedData" is treated as a child Resource object property.
  • Both primitive properties and nested Resource object properties can be accessed through Resource#getValueMap().get(String name).

Suppose you retrieved single Resource object through ResourceServiceBroker#resolve(...) method. Then you can easily access the properties and nested child resoruces like the following example:

ResourceServiceBroker broker = CrispHstServices.getDefaultResourceServiceBroker();
Resource product = resourceServiceBroker.resolve("demoProductCatalogs", "/products/sku/12345678901");
 
String name = (String) product.getValueMap().get("name");
String sku = (String) product.getValueMap().get("SKU");
String description = (String) product.getValueMap().get("description");
 
Resource extendedData = (Resource) product.getValueMap().get("extendedData");
String title = extendedData.getValueMap().get("title");
String type = extendedData.getValueMap().get("type");
String uri = extendedData.getValueMap().get("uri");
String description = extendedData.getValueMap().get("description");

The example code snippet shown above retrieved extendedData nested property first to retrieve properties of extendedData. Sometimes, you don't want to retrieve all the nested descendant Resource objects, especially if you have deeper descendant Resource properties. In that case, you can simply use Resource#getValue(String relPath) by passing a relative property path like the following example:

ResourceServiceBroker broker = CrispHstServices.getDefaultResourceServiceBroker();
Resource product = resourceServiceBroker.resolve("demoProductCatalogs", "/products/sku/12345678901");
 
// You can directly access "title" property of "extendedData" child resource object like this:
String title = product.getValue("extendedData/title");
// ...

Please see the JavaDoc Resource interface for detail.

Multiple Resource Collection from a JSON Array

Let's see a simple JSON data representing a collection of product data from a backend system:

[
  {
    "SKU": "12345678901",
    "description": "MultiSync X123BT - 109.22 cm (43 \") , 1920 x 480, 16:4, 500 cd\/m\u00b2, 3000:1, 8 ms",
    "name": "CBA MultiSync X123BT",
    "extendedData": {
      "title": "CBA MultiSync X123BT",
      "type": "Link",
      "uri": "Awesome-HIC-Site\/-\/products\/12345678901",
      "description": "MultiSync X123BT - 109.22 cm (43 \") , 1920 x 480, 16:4, 500 cd\/m\u00b2, 3000:1, 8 ms"
    }
  },
  {
    "SKU": "12345678902",
    "description": "PA123W, 68.58 cm (27 \") LCD, 2560 x 1440, 6ms, 1000:1, 300cd\/m2, 1.073B",
    "name": "CBA PA123W",
    "extendedData": {
      "title": "CBA PA123W",
      "type": "Link",
      "uri": "Awesome-HIC-Site\/-\/products\/12345678902",
      "description": "PA123W, 68.58 cm (27 \") LCD, 2560 x 1440, 6ms, 1000:1, 300cd\/m2, 1.073B"
    }
  },
  //...
]

The example JSON data shown above can be mapped to a Resource object as well, but the Resource object represents a collection (mapped to the root JSON Array object) instead.

  • The top level JSON Array is mapped to a Resource object. In this case, Resource#isArray() will return true.
  • Resource#getChildren() will return an iterable collection.

Suppose you retrieved a Resource object representing product collection data through ResourceServiceBroker#findResources(...) method. Then you can easily iterate resoruces like the following example:

ResourceServiceBroker broker = CrispHstServices.getDefaultResourceServiceBroker();
Resource products = resourceServiceBroker.findResources("demoProductCatalogs", "/products/");
 
for (Resource product : products.getChildren()) {
    String name = (String) product.getValueMap().get("name");
    String sku = (String) product.getValueMap().get("SKU");
    String description = (String) product.getValueMap().get("description");
    // ...
}

What if Multiple Resource Collection is embedded in a JSON Object, not as top-level JSON Array?

Let's see a JSON data example representing a JSON Object that embeds a collection of item data from a backend system. The following example returned from SalesForce REST API contains a JSON Object as top level object, and it contains JSON Array in the "records" property instead.

{
  "totalSize":27,
  "done":true,
  "records":[
    {
      "attributes":{
        "type":"Lead",
        "url":"/services/data/v20.0/sobjects/Lead/00Q460000020SQ7EAM"
      },
      "FirstName":"John",
      "LastName":"Doe",
      "Status":"Working - Contacted",
      "Title":"Director of Vendor Relations",
      "Industry":"Agriculture",
      "State":"FL",
      "Country":"USA",
      "City":"Tallahassee",
      "PostalCode":"32306",
      "Email":"john.doe@example.com",
      "Rating":"Hot"
    },
    {
      "attributes":{
        "type":"Lead",
        "url":"/services/data/v20.0/sobjects/Lead/00Q460000020SQ8EAM"
      },
      "FirstName":"Jane",
      "LastName":"Doe",
      "Status":"Open - Not Contacted",
      "Title":"CFO",
      "Industry":null,
      "State":"VA",
      "Country":"USA",
      "City":null,
      "PostalCode":null,
      "Email":"jane.doe@example.com",
      "Rating":null
    },
    // ...
  ]
}

The example JSON data shown above can be mapped to a Resource object too, but you need to retrieve the "records" Resource object property first and get a collection (mapped to the root JSON Array object) from there in this case.

final String SOQL_ALL_LEADS = "SELECT FirstName, LastName, Status, Title, Industry, Company, NumberOfEmployees, State, Country, City, "
        + "PostalCode, Email, IsDeleted, IsConverted, ConvertedAccountId, ConvertedContactId, Rating "
        + "FROM Lead";
 
ResourceServiceBroker broker = CrispHstServices.getDefaultResourceServiceBroker();
final Map<String, Object> pathVars = new HashMap<>();
pathVars.put("soql", SOQL_ALL_LEADS);
Resource result = broker.findResources("demoSalesForce", "/query/?q={soql}", pathVars);
Resource records = (Resource) result.getValueMap().get("records");
 
for (Resource lead : records.getChildren()) {
    String firstName = (String) lead.getValueMap().get("FirstName");
    String lastName = (String) lead.getValueMap().get("LastName");
    String email = (String) lead.getValueMap().get("Email");
    // ...
}

Pagination with Resource Object

Resource object supports paginating methods. For example, you can pass offset index and limit count of the paginated result on Resource#getChildren(long offset, long limit) like the following example to retrieve only specific set of the page:

// ...
Resource records = (Resource) result.getValueMap().get("records");
 
// The following will retrieve only the first page of the result when the page size is 10.
for (Resource lead : records.getChildren(0, 10)) {
    String firstName = (String) lead.getValueMap().get("FirstName");
    String lastName = (String) lead.getValueMap().get("LastName");
    String email = (String) lead.getValueMap().get("Email");
    // ...
}

Please see the JavaDoc of Resource interface for detail.