The Nuxeo Platform provides an extensible and customizable marshalling service: MarshallerRegistry This service allows you to use built-ins but also to register your own marshallers. The marshalling service facilitates the configuration of the rendered JSON, the aggregation of a marshaller from another and the possibility to extend or override existing marshallers.
The marshalling service is set up for the REST API v1 and the Automation server. Therefore, it is possible to operate all rendering parameters for the corresponding provided services.
The JSON generation and parsing is based on Jackson.
Creating Your Own Marshaller
The marshalling service allows you to create JSON-to-Java and Java-to-JSON marshallers. To create a marshaller, you have to write a Java class that manages the marshalling and registers it using a contribution.
Let's suppose we'd like to create marshallers for the following Java object:
public class Product {
private String reference;
public int getReference() { return reference; }
public void setReference(int reference) { this.reference = reference; }
private String description;
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
Creating a Java-to-JSON Marshaller
A Java-to-JSON marshaller must implement the org.nuxeo.ecm.core.io.registry.Writer<EntityType>
class. It must have a @Setup
annotation to register it as a marshaller and a @Supports
annotation to specify the supported mimetype (JSON in our example). The Product
class is a POJO, we can either use Jackson POJO marshalling capabilities or create a custom JSON marshalling.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class ProductJsonWriter extends AbstractJsonWriter<Product> {
public void write(Product product, JsonGenerator jg) throws IOException {
jg.writeObject(product);
}
}
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class ProductJsonWriter extends AbstractJsonWriter<Product> {
public void write(Product product, JsonGenerator jg) throws IOException {
jg.writeStartObject();
jg.writeNumberField("ref", product.getReference());
jg.writeStringField("desc", product.getDescription());
jg.writeEndObject();
}
}
- The
@Setup
annotation is here to define our object as a marshaller. - The
mode = Instanciations.SINGLETON
parameter defines that the marshaller must be instanciated once. - The
priority = Priorities.REFERENCE
parameter defines it as the default one for the Product class. - The
AbstractJsonWriter
super-class is the Java-to-JSON Marshaller's base class (it has the@Supports("application/json")
annotation). It helps to write the marshaller and to reuse the existing JSON marshalling.
Creating a JSON-to-Java Marshaller
A JSON-to-Java marshaller must implement the org.nuxeo.ecm.core.io.registry.Reader<EntityType>
class. It must have a @Setup
annotation to register it as a marshaller and a @Supports
annotation to specify the supported mimetype (JSON in our example). The Product class is a POJO, we can either use Jackson POJO marshalling capabilities or create a custom JSON marshalling.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class ProductJsonReader extends AbstractJsonReader<Product> {
public Product read(JsonNode jn) throws IOException {
return new ObjectMapper().readValue(jn, Product.class);
}
}
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class ProductJsonReader extends AbstractJsonReader<Product> {
public Product read(JsonNode jn) throws IOException {
Product product = new Product();
product.setReference(jn.get("reference").getIntValue());
product.setDescription(jn.get("description").getTextValue());
return product;
}
}
- The
@Setup
annotation is here to define our object as a marshaller. - The
mode = Instanciations.SINGLETON
parameter defines that the marshaller must be instanciated once. - The
priority = Priorities.REFERENCE
parameter defines it as the default one for theProduct
class. - The
AbstractJsonReader
super-class is the JSON-to-Java Marshaller's base class (it has the@Supports("application/json")
annotation). It helps to write the marshaller and to reuse the existing JSON marshalling.
Each marshaller supports injection of the RenderingContext
or any Nuxeo service using the `@java.inject.Injectannotation. The injected objects are inherited while extending an existing class. The
AbstractJsonWriterand the
AbstractJsonReaderprovide the RenderingContext ("ctx" attribute) and the
MarshallingRegistry` ("registry" attribute).
Managing Lists
The Nuxeo Platform provides built-in abstract classes to manage lists.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class ProductListJsonWriter extends DefaultListJsonWriter<Product> {
public ProductListJsonWriter() {
super("products", Product.class);
}
}
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class ProductListJsonReader extends DefaultListJsonReader<Product> {
public ProductListJsonReader() {
super("products", Product.class);
}
}
Registering Your Marshallers
The marshalling service provides an extension point : org.nuxeo.ecm.core.io.MarshallerRegistry/marshallers
. It allows to add new marshallers, override existing ones or unregister some.
<?xml version="1.0"?>
<component name="org.nuxeo.example.marshallers" version="1.0.0">
<extension target="org.nuxeo.ecm.core.io.MarshallerRegistry" point="marshallers">
<register class="org.nuxeo.example.ProductJsonWriter" enable="true" />
<register class="org.nuxeo.example.ProductJsonReader" enable="true" />
<register class="org.nuxeo.example.ProductListJsonWriter" enable="true" />
<register class="org.nuxeo.example.ProductListJsonReader" enable="true" />
</extension>
</component>
Using Your Marshallers
From a JAX-RS endpoint
Make sure your WebEngine application provides the nuxeo-core-io marshalling.
If it extends automation server or REST API v1, it's ok.
Otherwise, you have to register a single JAX-RS object in your application.
public class ProductApplication extends WebEngineModule { public Set<Object> getSingletons() { return Sets.newHashSet(new JsonCoreIODelegate()); } }
From your REST endpoints, just consume or return Product as usual.
Using marshallers@GET @Produces(MediaType.APPLICATION_JSON) public Product doGet() { Product product; // get the product return product; } @POST @Consumes(MediaType.APPLICATION_JSON) public Response doPost(Product product) { // do something with the product return Response.ok().build(); }
Outside JAX-RS
You can use the Nuxeo Platform marshalling service outside the web context. The simplest way is to use
org.nuxeo.ecm.core.io.registry.MarshallerHelper
.MarshallingHelper - Java-to-JSONProduct product = ...; String json = MarshallingHelper.objectToJson(product, CtxBuilder.get());
The
MarshallingHelper
class is a helper to use theMarshallingRegistry
service. This service provides multiple ways to get marshallers depending on your needs.MarshallingRegistry - Java-to-JSONProduct product = ...; OutputStream target = ...; MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); // get the most priorized JSON Writer for Product Writer<Product> writer = registry.getWriter(CtxBuilder.get(), Product.class, MediaType.APPLICATION_JSON_TYPE); writer.write(object, Product.class, Product.class, APPLICATION_JSON_TYPE, target); // get a specific JSON Writer instance ProductJsonWriter writer = registry.getInstance(CtxBuilder.get(), ProductJsonWriter.class); writer.write(object, Product.class, Product.class, APPLICATION_JSON_TYPE, target); // get the most priorized JSON Writer for Product // associated with the given context and available for use in multiple threads (usefull for use in listeners or schedulers) Writer<Product> writer = registry.getUniqueWriter(CtxBuilder.get(), Product.class, Product.class, MediaType.APPLICATION_JSON_TYPE); writer.write(object, Product.class, Product.class, APPLICATION_JSON_TYPE, target); // get a specific JSON Writer instance // associated with the given context and available for use in multiple threads (usefull for use in listeners or schedulers) ProductJsonWriter writer = registry.getUniqueInstance(CtxBuilder.get(), ProductJsonWriter.class); writer.write(object, Product.class, Product.class, APPLICATION_JSON_TYPE, target); // advanced use for Java class based on generic type List<Product> products = ...; // use TypeUtils from commons-lang3 to get the generic type Type productListType = TypeUtils.parametrize(List.class, Product.class) Writer<List<Product>> writer = registry.getWriter(CtxBuilder.get(), List.class, productListType, MediaType.APPLICATION_JSON_TYPE); writer.write(object, List.class, productListType, APPLICATION_JSON_TYPE, target);
From another marshaller: The easiest way to do that is to extends
AbstractJsonWriter
orAbstractJsonReader
.Java-to-JSON Aggregation@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE) public class AnObjectJsonWriter extends AbstractJsonWriter<AnObject> { public void write(AnObject anObject, JsonGenerator jg) throws IOException { jg.writeStartObject(); jg.writeStringField("someField", anObject.getSomeField()); Product product = anObject.getProduct(); // this will generates the product json using the provided Java-to-JSON marshaller writeEntityField("product", product, jg); // same things with 2 calls jg.writeFieldName("product"); writeEntity(product, jg); // or even, use the inherited "registry" attribute to get your marshaller and do what you want jg.writeEndObject(); } }
Parameterizing and Reusing Marshallers
The Nuxeo Platform marshalling service provides a rendering context. This context allows you to parametrize your JSON marshallers. For example you can decide to add a parameter which tells your marshaller to fetch or not some parts of the rendered object.
Usages
This context is managed by the class org.nuxeo.ecm.core.io.registry.context.RenderingContext
. A helper is provided to easily create a context: org.nuxeo.ecm.core.io.registry.context.RenderingContext.CtxBuilder
.
When the marshalling service is used from JAX-RS, the context is filled with:
- The Nuxeo URL
- The client locale
- The given HTTP headers
- The GET parameters
- The Java Request attributes
If you add parametrized behaviors to your marshaller, it is then customizable by the end-user application using the corresponding parameter in headers or in GET parameters.
@Setup(mode = SINGLETON, priority = Priorities.REFERENCE)
public class ProductJsonWriter extends AbstractJsonWriter<Product> {
public void write(Product product, JsonGenerator jg) throws IOException {
jg.writeStartObject();
jg.writeNumberField("ref", product.getReference());
if (ctx.getBooleanParameter("loadProductDescription")) {
jg.writeStringField("desc", product.getDescription());
}
String url = ctx.getBaseUrl() + "/productApp/product/ref/" + product.getReference();
jg.writeStringField("url", url);
jg.writeEndObject();
}
}
You can send the parameters using HTTP GET parameter or HTTP header (useful for POST/PUT/DELETE results).
$ curl -X GET 'http://localhost:8080/nuxeo/site/productApp/product/ref/10001?loadProductDescription=true'
$ curl -X GET -H 'loadProductDescription:true' 'http://localhost:8080/nuxeo/site/productApp/product/ref/10001'
You can also use the rendering context from the server side using the CtxBuilder
.
Product product = ...;
RenderindContext ctx = CtxBuilder.param("loadProductDescription", Boolean.TRUE).get();
String json = MarshallingHelper.objectToJson(product, ctx);
Finally, you can force the value of a parameter on the server side by overriding the rendering context used to produce the response.
@GET
@Produces(MediaType.APPLICATION_JSON)
public Product doGet(@Context HttpServletRequest request) {
Product product;
// get the product ... and configure output
RenderindContext ctx = CtxBuilder.param("loadProductDescription", Boolean.TRUE).get();
request.setAttribute(CTX_KEY, ctx);
return product;
}
private static final String CTX_KEY = "_STORED_GENERATED_RENDERING_CONTEXT";
RenderingContext API
Setting/Getting the Nuxeo Base URL
Web context usage | Retrieved from the HTTP call |
---|---|
Server side usage |
ctx = CtxBuilder.base("http://server/nuxeo").get(); |
Use from marshallers |
String url = ctx.getBaseUrl(); |
Setting/Getting the Current Locale
Web context usage | Retrieved from the end-user's browser |
---|---|
Server side usage |
ctx = CtxBuilder.locale(Locale.FRENCH).get(); |
Use from marshallers |
Locale locale = ctx.getLocale(); |
Delivering/Using a CoreSession to/in Marshallers
Web context usage | Retrieved from the WebContext, or from a document or created from a nxrepository parameter or created from a X-NXrepository header. |
---|---|
Server side usage |
CoreSession session = ...; ctx = CtxBuilder.session(session).get(); |
Use from marshallers |
The try-resource statement ensure the session would be closed if it's necessary
try (SessionWrapper wrapper = ctx.getSession(document)) {CoreSession session = wrapper.getSession(); } try (SessionWrapper wrapper = ctx.getSession(null)) {CoreSession session = wrapper.getSession(); } |
Setting/Getting Any Parameter
Web context usage |
URL:
|
---|---|
Server side usage |
ctx = CtxBuilder.param("paramName", "paramValue").get(); ctx.setParameterValues("paramName", "paramValue"); |
Use from marshallers |
Object o = ctx.getParameter("paramName"); List boolean b = ctx.getBooleanParameter("paramName"); |
Loading Some Document Properties
Web context usage |
URL:
|
---|---|
Server side usage |
ctx = CtxBuilder.properties("*").get(); ctx = CtxBuilder.properties("dublincore", "file").get(); ctx.setParameterValues("properties", "dublincore", "file"); |
Use from marshallers |
Set |
Loading Additional Parts of an Object Whose entity-type Is 'objectType'
The corresponding behavior depends on the marshaller.
Web context usage |
URL:
|
---|---|
Server side usage | Only for documents
ctx = CtxBuilder.fetchInDoc("part1", "part2").get(); ctx = CtxBuilder.fetch("objectType", "part1", "part2").get(); ctx.setParameterValues("fetch.objectType", "part1", "part2"); |
Use from marshallers |
Set |
Enabling Enricher for an objectType
The enricher will contribute to the object marshalling. The object marshaller must extend ExtensibleEntityJsonWriter
. The given enricher name must match an existing enricher for this object type.
Web context usage |
URL:
|
---|---|
Server side usage | Only for documents
ctx = CtxBuilder.enrichDoc("children", "acl").get(); ctx = CtxBuilder.enrich("objectType", "children", "acl").get(); ctx.setParameterValues("enrichers.objectType", "children", "acl"); |
Use from marshallers |
Set |
Translating Some Part of an Object Which May Contains Some l10n Key
Web context usage |
URL:
|
---|---|
Server side usage |
ctx = CtxBuilder.translate("objectType", "elementToTranslate").get(); ctx.setParameterValues("translate.objectType", "elementToTranslate"); |
Use from marshallers |
Set |
Controlling the Aggregation Depth
Available values are: root for no aggregation, children for 1 level, max for 2 levels. Default value is 'children'. See next chapter for more details
Web context usage |
URL:
|
---|---|
Server side usage |
ctx = CtxBuilder.depth(DepthValues.children).get(); ctx.setParameterValues("depth", "children"); |
Use from marshallers |
try (Closeable resource = ctx.wrap().controlDepth().open()) { // call another marshaller } catch (MaxDepthReachedException e) { // do not load properties } |
Aggregating Marshallers and Avoiding Infinite Loops
Let's take an example. We created two classes, Product
and Category
. And we'd like to marshall them as JSON.
When loading a category, we'd like to be able to get the corresponding products.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class CategoryJsonWriter extends AbstractJsonWriter<Category> {
public void write(Category category, JsonGenerator jg) throws IOException {
jg.writeStartObject();
jg.writeNumberField("ref", category.getReference());
jg.writeStringField("desc", category.getDescription());
if (ctx.getFetched("category").contains("products")) {
jg.writeArrayFieldStart("products");
for (Product product: product.getProducts()) {
// delegates the marshalling to Nuxeo
writeEntity(product);
}
jg.writeEndArray();
}
jg.writeEndObject();
}
}
$ curl -X GET 'http://localhost:8080/nuxeo/site/productApp/category/ref/12?fetch.category=products'
When loading a product, we'd like to be able to get the corresponding categories.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class ProductJsonWriter extends AbstractJsonWriter<Product> {
public void write(Product product, JsonGenerator jg) throws IOException {
jg.writeStartObject();
jg.writeNumberField("ref", product.getReference());
jg.writeStringField("desc", product.getDescription());
if (ctx.getFetched("product").contains("categories")) {
jg.writeArrayFieldStart("categories");
for (Category category: product.getCategories()) {
// delegates the marshalling to Nuxeo
writeEntity(category);
}
jg.writeEndArray();
}
jg.writeEndObject();
}
}
$ curl -X GET 'http://localhost:8080/nuxeo/site/productApp/category/ref/12?fetch.product=categories'
This works fine. But if we want to get both product's categories and category's products, there's a real risk to get an infinite loop. Or at least to get a very huge amount of JSON (if the category-product graph has no cycle).
$ curl -X GET 'http://localhost:8080/nuxeo/site/productApp/category/ref/12?fetch.product=categories&fetch.category=products'
To manage this case, we could use a counter put in the context but this will not work in most case because the counter will manage the first "line" of marshaller and disable the others: that's due to the processing which follows a "Depth First Search".
The Nuxeo Platform provides a specific object to wrap the depth control in each "crossing line". Each time you call another marshaller, you should use it.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class CategoryJsonWriter extends AbstractJsonWriter<Category> {
public void write(Category category, JsonGenerator jg) throws IOException {
jg.writeStartObject();
jg.writeNumberField("ref", category.getReference());
jg.writeStringField("desc", category.getDescription());
// control the graph depth
try (Closeable resource = ctx.wrap().controlDepth().open()) {
if (ctx.getFetched("category").contains("products")) {
jg.writeArrayFieldStart("products");
for (Product product: product.getProducts()) {
// delegates the marshalling to Nuxeo
writeEntity(product);
}
jg.writeEndArray();
}
} catch (MaxDepthReachedException e) {
// do not load products
}
jg.writeEndObject();
}
}
The max depth is controlled by the depth
parameter in the rendering context. The default value for depth is children
.
Depth values: If we get a product -> its categories -> their products ...
- root: No category will be loaded, just the first product.
- children: The product is loaded with its categories.
- max: The product is loaded, its categories also, and their products.
The only way to load more data is to avoid the use of depth control (which is bad!).
Each existing Nuxeo writer/enricher, which may use another marshaller, uses the depth's control.
Enriching / Extending / Overriding the Existing Marshallers
The Nuxeo Platform provides a flexible JSON marshalling. But sometimes, you'd like to add information from your Information System to the existing JSON or even override it to match your end-user needs.
There may be different needs:
Add information related to the marshalled object: Try to use built-in parameters, or else create an enricher.
Add existing informations to the JSON: Try to use built-in parameters, or else extend the existing marshaller.
- Change the existing JSON: Override the existing marshaller.
Enriching an Existing Marshaller
The enrichment mechanism is only enabled for the existing marshallers which extend ExtensibleEntityJsonWriter
. That means, almost all of them support enrichment.
An enrichable JSON object provides a contextParameters
field which contains all contributions. An enricher is activated if its name is specified in the parameters (for example: enrichers.document=breadcrumb,children
). The enricher must write 0, 1 or many key-value in the current JsonGenerator and must let the JsonGenerator in the same state it got it.
{
... // the existing generated object
contextParameters: { // only present if at least one enricher is activated
"theFirstEnricherKey": "theFirstEnricherValue",
"theSecondEnricherKey": { ... },
"theThirdEnricherKey-Entry1": { ... },
"theThirdEnricherKey-Entry2": { ... }
}
}
Let's take the breadcrumb
enricher as an example. It contributes to the generated JSON for a document and adds all of the parent documents.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class BreadcrumbJsonEnricher extends AbstractJsonEnricher<DocumentModel> { // <= enrich DocumentModel Java type
public BreadcrumbJsonEnricher() {
super("breadcrumb"); // <= is activated if "children" is in "enrichers.document" parameter's values
}
public void write(JsonGenerator jg, DocumentModel document) throws IOException {
List<DocumentModel> parentDocuments = null;
try (SessionWrapper wrapper = ctx.getSession(document)) {
parentDocuments = wrapper.getSession().getParentDocuments(document.getRef());
}
jg.writeFieldName("breadcrumb"); // <= write "breadcrumb" as key
writeEntity(documentList, jg); // delegates the parent documents marshalling to Nuxeo
}
}
Of course, like any JSON marshaller, you have to register it.
<?xml version="1.0"?>
<component name="org.nuxeo.example.marshallers" version="1.0.0">
<extension target="org.nuxeo.ecm.core.io.MarshallerRegistry" point="marshallers">
<register class="org.nuxeo.example.BreadcrumbJsonEnricher" enable="true" />
</extension>
</component>
That's all, you can now use it!
$ curl -X GET 'http://localhost:8080/nuxeo/site/api/v1/path/to/my/document?enrichers.document=breadcrumb'
All existing endpoints which return a DocumentModel will handle the registered enricher.
Extending an Existing JSON Marshaller
If you want to add information at the root level of the existing JSON (not in a contextParameters
sub object), you can extend an existing marshaller.
The extending mechanism is only enabled for the existing marshallers which extend ExtensibleEntityJsonWriter
. That means, almost all of them support it.
That could be dangerous if you use some JSON attributes which name already exists is the inherited JSON. Double check it!
Let's take an example with the DocumentModel. The current marshaller class for DocumentModel is DocumentModelJsonWriter
.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.OVERRIDE_REFERENCE) // <= an higher priority is used
public class MyCustomDocumentModelJsonWriter extends DocumentModelJsonWriter { // <= override the class
protected void extend(DocumentModel document, JsonGenerator jg) throws IOException { // <= just implement the inherited extend method
super.extend(document, jg); // optionnal
jg.writeStringField("additional", "information"); // feel free to write anything
}
}
Don't forget to register the marshaller. The resulting object will contain the whole existing JSON with your additional data.
All existing endpoints which return a DocumentModel will return the extended JSON.
Overriding an Existing JSON Marshaller
In this case, you want to completely rewrite an existing JSON.
Be careful with this approach, the marshallers provided by Nuxeo are used in many provided tools and screens. If you change it, you may break some existing components.
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.OVERRIDE_REFERENCE) // <= an higher priority is used
public class MyCustomDocumentModelJsonWriter extends AbstractJsonWriter<DocumentModel> {
public void write(DocumentModel document, JsonGenerator jg) throws IOException {
// generate what you want
}
}
JSON Marshallers Provided by the Nuxeo Platform
Each existing marshaller class provides a well documented Javadoc. Please, read the corresponding Javadoc to understand the generated JSON format and the available parameters.
document
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DocumentModel | Java-to-JSON | DocumentModelJsonWriter |
Writes a document as JSON. It is enrichable and extendable.
|
DocumentModel | JSON-to-Java | DocumentModelJsonReader |
Reads a document from JSON. Supports either reference or JSON object for extended fields value.
|
document / acls
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DocumentModel | Java-to-JSON Enricher | ACLJsonEnricher |
Enriches a document. Adds document ACLs.
Activated with enrichers.document=acls.
|
document / permissions
Managed Java Type | Type | Java Class | Behavior | < /tr>
---|---|---|---|
DocumentModel | Java-to-JSON Enricher | BasePermissionsJsonEnricher |
Enriches a document. Adds user's permission on the document.
Activated with enrichers.document=permissions .
|
document / breadcrumb
Managed Java type | Type | Java class | Behavior |
---|---|---|---|
DocumentModel | Java-to-JSON Enricher | BreadcrumbJsonEnricher |
Enriches a document. Add the parent's documents.
Activated with enrichers.document=breadcrumb .
|
document / children
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DocumentModel | Java-to-JSON Enricher | ChildrenJsonEnricher |
Enriches a document. Adds the children documents.
Activated with enrichers.document=children .
|
document / contextualParameters
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DocumentModel | Java-to-JSON Enricher | ContextualParametersJsonEnricher |
Enriches a document with free key/value pair. Only from the server side.
String name = "contextualParameters"; Map<String, String> keyValues = ...; ctx.enrich(name).param(name, keyValues).get(); |
document / preview
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DocumentModel | Java-to-JSON Enricher | PreviewJsonEnricher |
Enriches a document. Adds the URL of its preview.
Activated with enrichers.document=preview .
|
document / thumbnail
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DocumentModel | Java-to-JSON Enricher | ThumbnailJsonEnricher |
Enriches a document. Adds the URL of its thumbnail.
Activated with enrichers.document=thumbnail .
|
documents
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
List |
Java-to-JSON | DocumentModelListJsonWriter | Writes a list of document as JSON. Supports paginated lists and provides pagination information. Delegates the document's writing to the Nuxeo Platform. |
List |
JSON-to-Java | DocumentModelListJsonReader | Reads a list of document from JSON. Delegates the document's reading to the Nuxeo Platform. |
acls
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
ACP | Java-to-JSON | ACPJsonWriter | Writes a set of access right as JSON. It is enrichable and extendable |
docType
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DocumentType | Java-to-JSON | DocumentTypeJsonWriter | Writes a document's type as JSON. It is enrichable and extendable Delegates the writing of the type's schemas to the Nuxeo Platform. |
docTypes
Managed Java Type | < th colspan="1">TypeJava Class | Behavior | |
---|---|---|---|
List |
Java-to-JSON | DocumentTypeListJsonWriter | Writes a list of document's types as JSON. Supports paginated lists and provides pagination information. Delegates the type's writing to the Nuxeo Platform. |
facet
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
CompositeType | Java-to-JSON | FacetJsonWriter | Writes a document's facet as JSON. It is enrichable and extendable. Delegates the writing of the facet's schemas to the Nuxeo Platform. |
facets
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
List | Java-to-JSON | FacetListJsonWriter | Writes a list of facets as JSON. Supports paginated lists and provides pagination information. Delegates the facet's writing to the Nuxeo Platform. |
schema
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
Schema | Java-to-JSON | SchemaJsonWriter | Writes a schema as JSON. It is enrichable and extendable. |
schemas
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
List | Java-to-JSON | SchemaListJsonWriter | Writes a list of schemas as JSON. Supports paginated lists and provides pagination information. Delegates the schema's writing to the Nuxeo Platform. |
validation_constraint
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
Constraint | Java-to-JSON | ConstraintJsonWriter | Writes a validation constraint as JSON. It is enrichable and extendable. |
validation_constraints
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
List | Java-to-JSON | ConstraintListJsonWriter | Writes a list of constraints as JSON. Supports paginated lists and provides pagination information. Delegates the constraint's writing to the Nuxeo Platform. |
validation_report
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DocumentValidationReport | Java-to-JSON | DocumentValidationReportJsonWriter | Writes a validation report as JSON. It is enrichable and extendable. Delegates the constraint's writing to the Nuxeo Platform. |
user
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
NuxeoPrincipal | Java-to-JSON | NuxeoPrincipalJsonWriter | Writes a user as JSON. It is enrichable and extendable. |
NuxeoPrincipal | JSON-to-Java | NuxeoPrincipalJsonReader | Reads a user from JSON |
users
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
List | Java-to-JSON | NuxeoPrincipalListJsonWriter | Writes a list of users as JSON. Supports paginated lists and provides pagination information. Delegates the user's writing to the Nuxeo Platform. |
List | JSON-to-Java | NuxeoPrincipalListJsonReader | Reads a list of users from JSON. Delegates the user's reading to the Nuxeo Platform |
group
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
NuxeoGroup | Java-to-JSON | NuxeoGroupJsonWriter | Writes a group as JSON. It is enrichable and extendable. |
NuxeoGroup | JSON-to-Java | NuxeoGroupJsonReader | Reads a group from JSON |
groups
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
List | Java-to-JSON | NuxeoGroupListJsonWriter | Writes a list of groups as JSON. Supports paginated lists and provides pagination information. Delegates the group's writing to the Nuxeo Platform |
List | JSON-to-Java | NuxeoGroupListJsonReader | Reads a list of groups from JSON. Delegates the group's reading to the Nuxeo Platform. |
directoryEntry
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
DirectoryEntry | Java-to-JSON | DirectoryEntryJsonWriter |
Writes a directory entry as JSON. It is enrichable and extendable.
|
DirectoryEntry | JSON-to-Java | DirectoryEntryJsonReader | Reads a directory entry from JSON |
directoryEntries
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
List | Java-to-JSON | DirectoryEntryListJsonWriter | Writes a list of directory entries as JSON. Supports paginated lists and provides pagination information. Delegates the directory entries writing to the Nuxeo Platform. |
List | JSON-to-Java | DirectoryEntryListJsonReader | Reads a list of directory entries from JSON. Delegates the directory entries reading to the Nuxeo Platform. |
logEntry
Managed Java Type | Type | Java Class | Behavior |
---|---|---|---|
LogEntry | Java-to-JSON | LogEntryJsonWriter | Writes an audit entry to JSON. It is enrichable and extendable. |
List | Java-to-JSON | LogEntryListJsonWriter | Writes a list of audit entries to JSON It is enrichable and extendable. Delegates the entries writing to the Nuxeo Platform. |
Document JSON and Extended Fields
Nuxeo Platform 7.2 introduces the concept of Extended fields. See Field Constraints and Validation and How to Customize Document Validation.
This kind of document field are defined as reference to external object. For example, in the Dublin Core schema:
dc:creator
anddc:lastContributors
are references to Nuxeo usersdc:contributors
contains references to Nuxeo usersdc:subjects
contains references to the "l10nsubjects" directory entriesdc:coverage
is a reference to the "l10ncoverage" directory entrydc:nature
is a reference to the "nature" directory entry
Usage
The document marshalling provides a nice way to get the data referenced by one of its fields in-place.
For example, a call to get the JSON of a document which creator is the user "johnd" will return:
$ curl -X GET 'http://localhost:8080/nuxeo/site/api/v1/path/to/my/document?properties=dublincore'
{
"entity-type":"document",
"properties":{
"dc:creator": "johnd",
...
},
...
}
If we tell the document marshaller to load the dc:creator
referenced value, it will return the user's JSON in-place:
$ curl -X GET 'http://localhost:8080/nuxeo/site/api/v1/path/to/my/document?properties=dublincore&fetch.document=dc:creator'
{
"entity-type":"document",
"properties":{
"dc:creator":{
"entity-type":"user",
"id":"johndoe",
"properties":{
"firstName":"John",
"lastName":"Doe",
"password":"",
"groups":[
"members"
],
"company":"Nuxeo",
"email":"[email protected]",
"username":"johnd"
},
"extendedGroups":[
{
"name":"members",
"label":"Members group",
"url":"group/members"
}
],
"isAdministrator":false,
"isAnonymous":false
},
...
},
...
}
To load every extended fields of a document, use fetch.document=properties
.
The Nuxeo Platform provides resolver for document, directory entry, user and group. It also provides Java-to-JSON marshaller for those objects. Therefore, you can fetch any property based on built-in resolvers.
Updating Document Properties
If a field is defined as a reference and if you provide a JSON-to-Java marshaller for the referenced object, you can either update the document using the reference as usual or use the object's JSON to update the corresponding document property.
- The Nuxeo Platform uses the
ObjectResolver.getManagedClasses()
method to get the expected type. - The Nuxeo Platform delegates the JSON-to-Java marshalling to the marshalling service.
- The marshalling service delegates the JSON parsing to the registered marshaller.
- The Nuxeo Platform uses the resolver to get the reference from the parsed object and update the property.
The platform also provides JSON-to-Java for document, directory entry, user and group. Therefore, you can update any field based on the corresponding resolvers using the referenced object JSON.
Custom Resolver
When you create a custom resolver, if you want to be able to load the JSON of a referenced object, you have to register a Java-to-JSON marshaller.
If you want to be able to update a field using the JSON rather than the reference, you have to register a JSON-to-Java marshaller and be sure the getManagedClasses()
method of your resolver returns the expected Java type.
Testing JSON Data
The Nuxeo Platform offers a utility object to test JSON data: org.nuxeo.ecm.core.io.marshallers.json.JsonAssert
. It is provided by nuxeo-core-io test-jar.
<!-- JSON marshallers tests -->
<dependency>
<groupId>org.nuxeo.ecm.core</groupId>
<artifactId>nuxeo-core-io</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
Testing a Simple Marshaller
Let's test the following marshaller:
@Setup(mode = Instantiations.SINGLETON, priority = Priorities.REFERENCE)
public class ProductJsonWriter extends AbstractJsonWriter<Product> {
public void write(Product product, JsonGenerator jg) throws IOException {
jg.writeStartObject();
jg.writeNumberField("ref", product.getReference());
jg.writeStringField("desc", product.getDescription());
jg.writeEndObject();
}
}
The test looks like:
public class ProductJsonWriterTest extends AbstractJsonWriterTest.Local<ProductJsonWriter, Product> {
public ProductJsonWriterTest() {
super(ProductJsonWriter.class, Product.class);
}
@Test
public void test() throws Exception {
Product product = new Product(123, "a description");
JsonAssert json = jsonAssert(product);
json.isObject().properties(2);
json.has("ref").isEquals(product.getReference());
json.has("desc").isEquals(product.getDescription());
}
}
Each line makes an assertion on the provided JSON. Each assertion returns either the current JsonAssert (json.isObject().properties(2)
) or a new JsonAssert based on the referenced data (json.**has("reference")**.isEquals(product.getReference())
).
If your marshaller uses other marshallers, don't forget to load the corresponding contribution using @Deploy
or @LocalDeploy
.
Testing an Enricher
If you wrote an enricher for DocumentModel, the tested object should be DocumentModel. In that case, don't forget to load the contribution which contains your enricher registering.
public class SomeEnricherTest extends AbstractJsonWriterTest.Local<DocumentModelJsonWriter, DocumentModel> {
public ProductJsonWriterTest() {
super(DocumentModelJsonWriter.class, DocumentModel.class);
}
@Test
public void test() throws Exception {
DocumentModel doc = ...;
RenderingContext ctx = CtxBuilder.enrichDoc("someEnricher").get();
JsonAssert json = jsonAssert(doc, ctx);
json = json.has("contextParameters.someEnricher");
// make assertion on the enricher here
}
}
Testing Any JSON Data
You can also use JsonAssert
to test the result of your services.
public class SomeJsonTest {
@Test
public void test() throws Exception {
String jsonString = ...; // get JSON from somewhere
JsonAssert json = JsonAssert.on(jsonString);
// make assertion on the json here
}
}
Tips and Tricks
Getting the JSON
JsonAssert
provides the ability to get the json at any level.
{
"field1": { "field2": "hi!" },
"field3": "hello"
}
JsonAssert json = ...;
System.out.println( json.toString() );
// will print the whole JSON
System.out.println( json.has("field1.field2").toString() );
// will print "hi!" - be careful, the quotes are keeped, that's the real JSON
Quickly Testing Deep Data
JsonAssert
provides the ability to use JSON path.
{
"field1": {
"field2": [ {
"field3": "myDataShouldBeHere"
} ]
}
}
json.has("field1.field2[0].field3").isEquals("myDataShouldBeHere");
Quickly Testing Array Values
JsonAssert
provides the ability to test array values.
{
"field1": [ "A should be here", "B should be here", "C should be here twice", "C should be here twice" ]
}
json.has("field1").contains("A should be here", "B should be here", "C should be here twice", "C should be here twice");
Quickly Testing Complex Structure
JsonAssert
provides the ability to test the value of a field for multiple object in an array.
{
"field1": [
{ "field2": "A is here" },
{ "field2": "B is here twice" },
{ "field2": "B is here twice" }
]
}
json.childrenContains("field1.field2", "A is here", "B is here twice", "B is here twice");
JsonAssert.childrenContains
can cross over multiple arrays and objects.