# Holon JSON support

version 5.0.1

Copyright © 2016-2017

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

## 1. Introduction

The Holon Platform JSON module provides integration support between the platform foundation PropertyBox data structure and the most popular JSON processing libraries:

- Gson

- Jackson

The module faces the following integration concerns:

- Gson GsonBuilder configuration

- Jackson ObjectMapper configuration

- JAX-RS integration and auto-configuration

- Spring RestTemplate configuration

- Spring boot auto-configuration

## 2. Obtaining the artifacts

The Holon Platform uses Maven for projects build and configuration. All the platform artifacts are published in the Maven Central Repository, so there is no need to explicitly declare additional repositories in your project `pom` file.

At the top of each section of this documentation you will find the Maven coordinates (group id, artifact id and version) to obtain the artifact(s) as a dependency for your project.

A BOM (Bill Of Materials) `pom` is provided to import the available dependencies for a specific version in your projects. The Maven coordinates for the core BOM are the following:

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-json-bom</artifactId>
    <version>5.0.1</version>

The BOM can be imported in a Maven project in the following way:

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>com.holon-platform.json</groupId>
          <artifactId>holon-json-bom</artifactId>
          <version>5.0.1</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>

### 2.1. Using the Platform BOM

The Holon Platform provides an overall Maven BOM (Bill of Materials) to easily obtain all the available platform artifacts.

See Obtain the platform artifacts for details.

## 3. Dealing with properties and `PropertySet`

A PropertyBox is a versatile and general-purpose data container object which uses Property type keys as reference for the data it manages.

The Properties to which data are bound, i.e. the `PropertySet` associated with the `PropertyBox`, are complex and extensible objects, highly customizable by the developer. For this reason, a `Property` is never serialized in JSON by this module.

### 3.1. Serialization

Only `Path` properties are serialized into JSON, using the path name as property name of the produced JSON object and JSON data type consistent with the property type.

This way, the JSON definitions produced by the serialization of a `PropertyBox` are fully analogous to a standard JSON object, composed by a list of property names associated to a value of a suitable type, which can be eventually deserialized in a generic data object (with a consistent internal structure) even a language different from Java.

### 3.2. Deserialization

To deserialize a JSON object into a `PropertyBox` instance, the `PropertySet` to use for the `PropertyBox` is required and must be available as a Context resource (typically thread-bound).

To bound a `PropertySet` instance to the default thread-scoped resource set of the Holon Platform Context, the `execute(Callable operation)` method can be used.

See the next sections for further details and use-case examples.

## 4. Gson integration

A `JsonSerializer` and a `JsonDeserializer` are provided to configure PropertyBox serialization to JSON format and deserialization from JSON format.

### 4.1. Configuration and use

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-gson</artifactId>
    <version>5.0.1</version>

To enable `PropertyBox` handling in Gson, registering the suitable `JsonSerializer` and a `JsonDeserializer` pair, the GsonConfiguration utility class can be used:

    GsonBuilder builder = GsonConfiguration.builder(); (1)
    Gson gson = builder.create();

    GsonBuilder mybuilder = getGsonBuilder(); (2)
    GsonConfiguration.configure(builder); (3)
    gson = mybuilder.create();

1 | Get a pre-configured `GsonBuilder` with `PropertyBox` support
2 | Get a previously available `GsonBuilder`
3 | Configure the builder for `PropertyBox` support

With a properly configured `Gson` instance, a `PropertyBox` can be serialized and deserialized just like any another object. As described in Dealing with properties and `PropertySet`, you need to declare the `PropertySet` to use as a Context resource at `PropertyBox` deserialization time.

    final static PathProperty<Long> ID = PathProperty.create("id", Long.class);
    final static PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);

    final static PropertySet<?> PROPERTY_SET = PropertySet.of(ID, DESCRIPTION);

    public void serializeAndDeserialize() {
      Gson gson = GsonConfiguration.builder().create(); (1)

      PropertyBox box = PropertyBox.builder(PROPERTY_SET).set(ID, 1L).set(DESCRIPTION, "Test").build(); (2)

      // serialize
      String json = gson.toJson(box); (3)
      // deserialize
      box = PROPERTY_SET.execute(() -> gson.fromJson(json, PropertyBox.class)); (4)
    }

1 | Obtain a pre-configured `Gson` instance
2 | Build a `PropertyBox` using `PROPERTY_SET` as property set
3 | Serialize the `PropertyBox` to JSON.
4 | Deserialize back the JSON definition to a `PropertyBox` instance using `PROPERTY_SET` as property set, declaring it as thread-bound Context resource through the `execute(…​)` method

In the example above, the `PropertyBox` instance will be serialized as a JSON object like this:

    {
      "id": 1,
      "description": "Test"
    }

### 4.2. JAX-RS integration

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-gson-jaxrs</artifactId>
    <version>5.0.1</version>

A set of JAX-RS extension classes are provided to configure `PropertyBox` JSON support in JAX-RS using Gson, and in a more general sense, to configure Gson as main serializer/deserializer in a JAX-RS server and/or client for the JSON media type.

The JAX-RS Gson extensions relies on standard `ContextResolver` type to provide the `Gson` instance to use to perform JSON serialization and deserialization. This `Gson` instance must be configured with `PropertyBox` support as described above to handle `PropertyBox` types consistently.

To setup Gson as provider for JSON message type handling, the GsonFeature JAX-RS `Feature` can be registered in the JAX-RS application.

If you use Jersey or Resteasy as JAX-RS implementation, there is no need to explicitly register the `GsonFeature`, just ensure the `holon-gson` jar is in classpath and the Gson support will be configured automatically, leveraging Jersey AutoDiscoverable and Resteasy Java Service extensions features.

#### 4.2.1. `PropertyBox` deserialization

When a `PropertyBox` is used as a JAX-RS resource method parameter (for methods which declare to consume `application/json` media type), the JSON deserialization of the input into a `PropertyBox` instance needs to know the `PropertySet` to use in order to create the property box. For this purpose, the `@PropertySetRef` annotation can be used at method parameter level to declare the `PropertySet` instance to use to deserialize the property box.

The PropertySetRef annotation allows to declare the `PropertySet` instance as the `public static` field of a given class, which must be specified in the `value()` annotation attribute. If more than one `public static` field of `PropertySet` type is present in declared class, the `field()` annotation attribute can be used to specify the right field name.

    final static PathProperty<Integer> CODE = PathProperty.create("code", Integer.class);
    final static PathProperty<String> NAME = PathProperty.create("name", String.class);

    final static PropertySet<?> PROPERTYSET = PropertySet.of(CODE, NAME);

    // JAX-RS example endpoint
    @Path("test")
    public static class Endpoint {

      @PUT
      @Path("serialize")
      @Consumes(MediaType.APPLICATION_JSON)
      public Response create(@PropertySetRef(value = ExampleGson.class, field = "PROPERTYSET") PropertyBox data) {
        return Response.accepted().build();
      }

      @GET
      @Path("deserialize")
      @Produces(MediaType.APPLICATION_JSON)
      public PropertyBox getData() {
        return PropertyBox.builder(PROPERTYSET).set(CODE, 1).set(NAME, "Test").build();
      }

    }

    public void jaxrs() {
      Client client = ClientBuilder.newClient(); (1)

      PropertyBox box1 = PropertyBox.builder(PROPERTYSET).set(CODE, 1).set(NAME, "Test").build();

      client.target("https://host/test/serialize").request().put(Entity.entity(box1, MediaType.APPLICATION_JSON)); (2)

      PropertyBox box2 = PROPERTYSET
          .execute(() -> client.target("https://host/test/deserialize").request().get(PropertyBox.class)); (3)

    }

1 | Create a JAX-RS `Client`
2 | Perform a `PUT` request providing a `PropertyBox` value as JSON. At the endpoint resource level, the `PropertyBox` type input parameter of the `serialize` method is annotated with `@PropertySetRef` in order to declare the property set to use to deserialize the property box from JSON
3 | Perform a `GET` request for a JSON serialized `PropertyBox` value, providing the `PropertySet` to use for deserialization as a `Context` thread-bound resource

#### 4.2.2. JAX-RS integration configuration

The following configuration properties are available to tune or disable the JAX-RS integration features for Gson and `PropertyBox` support:

- `holon.gson.disable-resolver`: If this property is present in JAX-RS application properties, the Gson ContextResolver auto-configuration is disabled

- `holon.gson.disable-autoconfig`: If this property is present in JAX-RS application properties, all the Gson and `PropertyBox` JSON serialization/deserialization features will be disabled

- `holon.jaxrs.json.pretty-print`: If `true`, enables pretty printing of serialized JSON

### 4.3. Spring integration

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-gson-spring</artifactId>
    <version>5.0.1</version>

The SpringGsonConfiguration utility class can be used to configure a Spring `RestTemplate`, ensuring that a `GsonHttpMessageConverter` is registered and bound to a `Gson` instance correctly configured for `PropertyBox` JSON serialization/deserialization.

    class Config {

      @Bean
      public RestTemplate restTemplate() {
        RestTemplate rt = new RestTemplate();
        SpringGsonConfiguration.configure(rt); (1)
        return rt;
      }

    }

1 | Configure the `RestTemplate`

### 4.4. Spring Boot integration

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-gson-spring</artifactId>
    <version>5.0.1</version>

The `GsonAutoConfiguration` Spring Boot auto-configuration class is provided to automatically configure a `Gson` instance singleton bean, correctly configured for `PropertyBox` JSON serialization/deserialization.

This way, the `RestTemplate` instances obtained through the `RestTemplateBuilder` Spring Boot builder will be automatically pre-configured with a `Gson` message converter with `PropertyBox` support.

| The `Gson` auto-configuration is triggered only if a `Gson` type bean is not already registered in Spring context.

To disable this auto-configuration feature the `GsonAutoConfiguration` class can be excluded:

    @EnableAutoConfiguration(exclude={GsonAutoConfiguration.class})

## 5. Jackson integration

A `JsonSerializer` and a `JsonDeserializer` are provided to configure PropertyBox serialization to JSON format and deserialization from JSON format for a Jackson `ObjectMapper`.

### 5.1. Configuration and use

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-jackson</artifactId>
    <version>5.0.1</version>

To enable `PropertyBox` handling in Jackson, registering the suitable `JsonSerializer` and a `JsonDeserializer` pair, the JacksonConfiguration utility class can be used:

    ObjectMapper mapper = new ObjectMapper(); (1)

    JacksonConfiguration.configure(mapper); (2)

1 | Get or create a Jackson `ObjectMapper`
2 | Configure the `ObjectMapper` for `PropertyBox` support

With a properly configured `ObjectMapper` instance, a `PropertyBox` can be serialized and deserialized just like any another object. As described in Dealing with properties and `PropertySet`, you need to declare the `PropertySet` to use as a Context resource at `PropertyBox` deserialization time.

    final static PathProperty<Long> ID = PathProperty.create("id", Long.class);
    final static PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);

    final static PropertySet<?> PROPERTY_SET = PropertySet.of(ID, DESCRIPTION);

    public void serializeAndDeserialize() throws JsonProcessingException {
      ObjectMapper mapper = new ObjectMapper();
      JacksonConfiguration.configure(mapper); (1)

      PropertyBox box = PropertyBox.builder(PROPERTY_SET).set(ID, 1L).set(DESCRIPTION, "Test").build(); (2)

      // serialize
      String json = mapper.writer().writeValueAsString(box); (3)
      // deserialize
      box = PROPERTY_SET.execute(() -> mapper.reader().forType(PropertyBox.class).readValue(json)); (4)
    }

1 | Use a properly configured `ObjectMapper` instance
2 | Build a `PropertyBox` using `PROPERTY_SET` as property set
3 | Serialize the `PropertyBox` to JSON.
4 | Deserialize back the JSON definition to a `PropertyBox` instance using `PROPERTY_SET` as property set, declaring it as thread-bound Context resource through the `execute(…​)` method

In the example above, the `PropertyBox` instance will be serialized as a JSON object like this:

    {
      "id": 1,
      "description": "Test"
    }

### 5.2. JAX-RS integration

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-jackson-jaxrs</artifactId>
    <version>5.0.1</version>

A set of JAX-RS extension classes are provided to configure `PropertyBox` JSON support in JAX-RS using Jackson.

The JAX-RS Jackson extensions relies on standard `ContextResolver` type to provide a properly configured `ObjectMapper` instance to use to perform JSON serialization and deserialization.

To setup Jackson for `PropertyBox` handling in JSON message type, the JacksonFeature JAX-RS `Feature` can be registered in the JAX-RS application.

If you use Jersey or Resteasy as JAX-RS implementation, there is no need to explicitly register the `JacksonFeature`, just ensure the `holon-jackson` jar is in classpath and the Jackson support will be configured automatically, leveraging Jersey AutoDiscoverable and Resteasy Java Service extensions features.

#### 5.2.1. `PropertyBox` deserialization

When a `PropertyBox` is used as a JAX-RS resource method parameter (for methods which declare to consume `application/json` media type), the JSON deserialization of the input into a `PropertyBox` instance needs to know the `PropertySet` to use in order to create the property box. For this purpose, the `@PropertySetRef` annotation can be used at method parameter level to declare the `PropertySet` instance to use to deserialize the property box.

The PropertySetRef annotation allows to declare the `PropertySet` instance as the `public static` field of a given class, which must be specified in the `value()` annotation attribute. If more than one `public static` field of `PropertySet` type is present in the declared class, the `field()` annotation attribute can be used to specify the right field name.

    final static PathProperty<Integer> CODE = PathProperty.create("code", Integer.class);
    final static PathProperty<String> NAME = PathProperty.create("name", String.class);

    final static PropertySet<?> PROPERTYSET = PropertySet.of(CODE, NAME);

    // JAX-RS example endpoint
    @Path("test")
    public static class Endpoint {

      @PUT
      @Path("serialize")
      @Consumes(MediaType.APPLICATION_JSON)
      public Response create(@PropertySetRef(value = ExampleJackson.class, field = "PROPERTYSET") PropertyBox data) {
        return Response.accepted().build();
      }

      @GET
      @Path("deserialize")
      @Produces(MediaType.APPLICATION_JSON)
      public PropertyBox getData() {
        return PropertyBox.builder(PROPERTYSET).set(CODE, 1).set(NAME, "Test").build();
      }

    }

    public void jaxrs() {
      Client client = ClientBuilder.newClient(); (1)

      PropertyBox box1 = PropertyBox.builder(PROPERTYSET).set(CODE, 1).set(NAME, "Test").build();

      client.target("https://host/test/serialize").request().put(Entity.entity(box1, MediaType.APPLICATION_JSON)); (2)

      PropertyBox box2 = PROPERTYSET
          .execute(() -> client.target("https://host/test/deserialize").request().get(PropertyBox.class)); (3)

    }

1 | Create a JAX-RS `Client`
2 | Perform a `PUT` request providing a `PropertyBox` value as JSON. At the endpoint resource level, the `PropertyBox` type input parameter of the `serialize` method is annotated with `@PropertySetRef` in order to declare the property set to use to deserialize the property box from JSON
3 | Perform a `GET` request for a JSON serialized `PropertyBox` value, providing the `PropertySet` to use for deserialization as a `Context` thread-bound resource

#### 5.2.2. JAX-RS integration configuration

The following configuration properties are available to tune or disable the JAX-RS integration features for Jackson `PropertyBox` support:

- `holon.jackson.disable-resolver`: If this property is present in JAX-RS application properties, the Jackson ContextResolver auto-configuration is disabled

- `holon.jackson.disable-autoconfig`: If this property is present in JAX-RS application properties, all the Jackson `ObjectMapper` and `PropertyBox` JSON serialization/deserialization features will be disabled

- `holon.jaxrs.json.pretty-print`: If `true`, enables pretty printing of serialized JSON

### 5.3. Spring integration

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-jackson-spring</artifactId>
    <version>5.0.1</version>

The SpringJacksonConfiguration utility class can be used to configure a Spring `RestTemplate`, ensuring that a `MappingJackson2HttpMessageConverter` is registered and bound to a `ObjectMapper` instance correctly configured for `PropertyBox` JSON serialization/deserialization.

    class Config {

      @Bean
      public RestTemplate restTemplate() {
        RestTemplate rt = new RestTemplate();
        SpringJacksonConfiguration.configure(rt); (1)
        return rt;
      }

    }

1 | Configure the `RestTemplate`

### 5.4. Spring Boot integration

Maven coordinates:

    <groupId>com.holon-platform.json</groupId>
    <artifactId>holon-jackson-spring</artifactId>
    <version>5.0.1</version>

The `JacksonAutoConfiguration` Spring Boot auto-configuration class is provided to automatically configure an `ObjectMapper` for `PropertyBox` JSON serialization/deserialization. An `ObjectMapper` instance must be available as bean in the Spring context.

This way, the `RestTemplate` instances obtained through the `RestTemplateBuilder` Spring Boot builder will be automatically pre-configured with a JSON message converter with `PropertyBox` support.

To disable this auto-configuration feature the `JacksonAutoConfiguration` class can be excluded:

    @EnableAutoConfiguration(exclude={JacksonAutoConfiguration.class})

## 6. Loggers

By default, the Holon platform uses the SLF4J API for logging. The use of SLF4J is optional: it is enabled when the presence of SLF4J is detected in the classpath. Otherwise, logging will fall back to JUL (`java.util.logging`).

The logger names for the JSON module are:

- `com.holonplatform.json.gson` for the Gson integration classes

- `com.holonplatform.json.jackson` for the Jackson integration classes

## 7. System requirements

### 7.1. Java

The Holon Platform JSON module requires Java 8 or higher.