POJO Mapping Support
Introduction
As Resource objects are in the very thin layer on top of the backend data objects (JSON, XML, etc.), CRISP API supports a simple POJO mapping feature for both JSON and XML data objects. For underlying JSON data objects, it uses Jackson library for POJO mapping and so it supports optional Jackson annotations in the POJO classes. For underlying XML data objects, it uses JAXB library for POJO mapping and so it support optional JAXB annotations in the POJO classes.
Mapping Single Resource Object
Whether the backend data objects are based on JSON or XML, you need to retrieve org.onehippo.cms7.crisp.api.resource.ResourceBeanMapper object from the ResourceServiceBroker and then you can convert a Resource object or a ResourceCollection object to a POJO or a collection of POJOs like the following example:
// Suppose you retrieve a Resource object through resourceServiceBroker. Resource resource = resourceServiceBroker.resolve(resourceSpace, ...); // Now, you need to get the ResourceBeanMapper first from resourceServiceBroker. ResourceBeanMapper resourceBeanMapper = resourceServiceBroker.getResourceBeanMapper(resourceSpace); // And then you can convert the resource object to a POJO, of Product type. Product product = resourceBeanMapper.map(resource, Product.class);
Depending on whether your backend data objects are based on JSON or XML, the POJO class could have different annotations for mapping.
Mapping Resource Collection
The ResourceBeanMapper supports POJO mapping for ResourceCollection as well, to produce a collection of POJOs too:
// Suppose you retrieve a Resource object which contains a collection of the child product resource objects. Resource productsRootResource = resourceServiceBroker.resolve(resourceSpace, ...); // Now, you can retrieve the child collection, each item of which represents a product resource. ResourceCollection resCol = productsRootResource.getChildren(); // And then you can convert all the child product resource objects to a collection of POJOs, of Product type. Collection<Product> productsCollection = resourceBeanMapper.mapCollection(resCol, Product.class);
There are different variations of ResourceBeanMapper#mapCollection(...) for optional pagination and a custom collection instantiation supports. See the simplified Javadocs below for details.
/** * Map a {@link Resource} object to a bean of {@code type}. */ public <T> T map(Resource resource, Class<T> beanType) throws ResourceException; /** * Map all the child resources of the {@link ResourceCollection} to a new collection of {@code beanType} to return. */ public <T> Collection<T> mapCollection(ResourceCollection resourceCollection, Class<T> beanType) throws ResourceException; /** * Map the child resources of the {@link ResourceCollection} from the {@code offset} index * up to {@code limit} size at max to a new collection of {@code beanType} to return. */ public <T> Collection<T> mapCollection(ResourceCollection resourceCollection, Class<T> beanType, int offset, int limit) throws ResourceException; /** * Map all the child resources of the {@link ResourceCollection} and push them to * the given {@code targetBeanCollection} of {@code beanType}. */ public <T> void mapCollection(ResourceCollection resourceCollection, Class<T> beanType, Collection<T> targetBeanCollection) throws ResourceException; /** * Map the child resources of the {@link ResourceCollection} from the {@code offset} index * up to {@code limit} size at max and push them to the given {@code targetBeanCollection}. */ public <T> void mapCollection(ResourceCollection resourceCollection, Class<T> beanType, Collection<T> targetBeanCollection, int offset, int limit) throws ResourceException;
POJO Mapping for JSON-based Resources
Suppose the underlying backend data for a Resource object looks like the following JSON example:
{ "SKU": "4150349", "description": "MultiSync X431BT - 109.22 cm (43 \") , 1920 x 480, 16:4, 500 cd\/m\u00b2, 3000:1, 8 ms", "name": "NEC MultiSync X431BT", "extendedData": { "title": "NEC MultiSync X431BT", "type": "Link", "uri": "Incentro-HIC-Site\/-\/products\/4150349", "description": "MultiSync X431BT - 109.22 cm (43 \") , 1920 x 480, 16:4, 500 cd\/m\u00b2, 3000:1, 8 ms" } }
Now, you could define POJO classes for the product resource data with Jackson annotations like the following examples:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) public class Product { private String sku; private String description; private String name; private ExtendedData extendedData; @JsonProperty("SKU") public String getSKU() { return sku; } public void setSKU(String value) { this.sku = value; } public String getDescription() { return description; } public void setDescription(String value) { this.description = value; } public String getName() { return name; } public void setName(String value) { this.name = value; } public ExtendedData getExtendedData() { return extendedData; } public void setExtendedData(ExtendedData value) { this.extendedData = value; } }
Note that it contains specific Jackson annotations to ignore any unresolvable JSON properties and clarify JSON property mapping. Please see Jackson2 documentations for details on POJO mapping annotations.
public class ExtendedData { private String title; private String type; private String uri; private String description; public String getTitle() { return title; } public void setTitle(String value) { this.title = value; } public String getType() { return type; } public void setType(String value) { this.type = value; } public String getUri() { return uri; } public void setUri(String value) { this.uri = value; } public String getDescription() { return description; } public void setDescription(String value) { this.description = value; } }
Now, you can convert a product Resource object to Product instance like the following example.
// Suppose you retrieved a product Resource object. Resource resource = resourceServiceBroker.resolve(resourceSpace, ...); // Get the ResourceBeanMapper first from resourceServiceBroker. ResourceBeanMapper resourceBeanMapper = resourceServiceBroker.getResourceBeanMapper(resourceSpace); // And then you can convert the resource object to a POJO, of Product type. Product product = resourceBeanMapper.map(resource, Product.class);
The collection mapping operations (ResourceBeanMapper#mapCollection(...)) works in the same way on each effective iteration.
POJO Mapping for XML-based Resources
Suppose the underlying backend data for a Resource object looks like the following XML example:
<product> <SKU>4150349</SKU> <description>MultiSync X431BT - 109.22 cm (43 ") , 1920 x 480, 16:4, 500 cd/m², 3000:1, 8 ms</description> <name>NEC MultiSync X431BT</name> <extendedData> <title>NEC MultiSync X431BT</title> <type>Link</type> <uri>Incentro-HIC-Site/-/products/4150349</uri> <description>MultiSync X431BT - 109.22 cm (43 ") , 1920 x 480, 16:4, 500 cd/m², 3000:1, 8 ms</description> </extendedData> </product>
Now, you could define POJO classes for the product resource data with JAXB annotations like the following examples:
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name="product") public class Product { private String sku; private String description; private String name; private ExtendedData extendedData; @XmlElement(name = "SKU") public String getSKU() { return sku; } public void setSKU(String value) { this.sku = value; } @XmlElement public String getDescription() { return description; } public void setDescription(String value) { this.description = value; } @XmlElement public String getName() { return name; } public void setName(String value) { this.name = value; } @XmlElement public ExtendedData getExtendedData() { return extendedData; } public void setExtendedData(ExtendedData value) { this.extendedData = value; } }
Note that it contains specific Jackson annotations to clarify XML elements/attributes mapping. Please see JAXB documentations for details on POJO mapping annotations.
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name="extendedData") public class ExtendedData { private String title; private String type; private String uri; private String description; @XmlElement public String getTitle() { return title; } public void setTitle(String value) { this.title = value; } @XmlElement public String getType() { return type; } public void setType(String value) { this.type = value; } @XmlElement public String getUri() { return uri; } public void setUri(String value) { this.uri = value; } @XmlElement public String getDescription() { return description; } public void setDescription(String value) { this.description = value; } }
Now, you can convert a product Resource object to Product instance like the following example.
// Suppose you retrieved a product Resource object. Resource resource = resourceServiceBroker.resolve(resourceSpace, ...); // Get the ResourceBeanMapper first from resourceServiceBroker. ResourceBeanMapper resourceBeanMapper = resourceServiceBroker.getResourceBeanMapper(resourceSpace); // And then you can convert the resource object to a POJO, of Product type. Product product = resourceBeanMapper.map(resource, Product.class);
The collection mapping operations (ResourceBeanMapper#mapCollection(...)) works in the same way on each effective iteration.
Summary
CRISP API supports a simple POJO mapping feature for both JSON and XML data objects, based on either Jackson or JAXB POJO mapping library. So, you can use either Jackson annotations or JAXB annotations to control how to map data to POJO objects.
You need to retrieve a Resource or ResourceCollection object first and then convert it to either a POJO or a collection of POJOs through ResourceBeanMapper object which can be retrieved from ResourceServiceBroker instance for the resource space.