Skip to content

SDA Commons Server Jackson

javadoc

The module sda-commons-server-jackson is used to configure the ObjectMapper with the recommended default settings of SDA SE services. It also provides support for linking resources with HAL and adds the ability to filter fields on client request.

The JacksonConfigurationBundle is used to configure the JSON serializer. It adds various error mappers to support the SDA error message standard. These replace the default Dropwizard error mappers but also additional new mappers are added, e.g. mapping JaxRs Exceptions, such as NotFound and NotAuthorized. All mappers do log the errors when mapping. The framework already provides a predefined exception mapper for InvalidTypeException if the framework default for feature FAIL_ON_INVALID_SUBTYPE is customized. See section Customize the ObjectMapper below for details about customizing the default object mapper settings. If the feature is enabled the InvalidTypeIdExceptionMapper produces a response in the common error format.

The ObjectMapperConfigurationUtil can be used to receive an ObjectMapper with the recommended settings for usage outside a Dropwizard application.

The default ObjectMapper is configured to be fault-tolerant to avoid failures in deserialization. JSR-303 validations should be used to validate input data. For serialization the bundle disables FAIL_ON_EMPTY_BEANS, WRITE_DATES_AS_TIMESTAMPS, WRITE_DURATIONS_AS_TIMESTAMPS. For deserialization the bundle disables FAIL_ON_UNKNOWN_PROPERTIES, FAIL_ON_IGNORED_PROPERTIES, FAIL_ON_INVALID_SUBTYPE and enables ACCEPT_SINGLE_VALUE_AS_ARRAY, READ_UNKNOWN_ENUM_VALUES_AS_NULL, READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE. The FuzzyEnumModule from Dropwizard is removed as it lacks support of newer Jackson features for enumerations.

Usage

In the application class, the bundle is added in the initialize method:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import JacksonConfigurationBundle;
import io.dropwizard.core.Application;

public class MyApplication extends Application<MyConfiguration> {

    public static void main(final String[] args) {
        new MyApplication().run(args);
    }

   @Override
   public void initialize(Bootstrap<MyConfiguration> bootstrap) {
      // ...
      bootstrap.addBundle(JacksonConfigurationBundle.builder().build());
      // ...
   }

   @Override
   public void run(MyConfiguration configuration, Environment environment) {
      // ...
   }
}

Date and Time formatting

It is strongly recommended to use

  • LocalDate for dates without time
  • ZonedDateTime for date and times
  • Duration for durations with time resolution
  • Period for durations with day resolution

All these types can be read and written in JSON as ISO 8601 formats. ZonedDateTime is formatted with milliseconds or nanoseconds according to the detail set in the instance.

Type Resolution Example
LocalDate Day of Month 2018-09-23
ZonedDateTime Any time unit 2018-09-23T14:21:41.123+01:00
Duration Any time unit P1DT13M
Period Days P1Y2D

Reading ZonedDateTime is configured to be tolerant so that added nanoseconds or missing milliseconds or missing seconds are supported.

Please do not use @JsonFormat(pattern = "...") for customizing serialization because it breaks tolerant reading of formatting variants. If output should be customized, use @JsonSerializer.

SDA-Commons provides two default serializers for ZonedDateTime to use a fixed resolution in the output. Iso8601Serializer is used to omit milliseconds and Iso8601Serializer.WithMillis is used to render the time with 3 digits for milliseconds.

Usage:

1
2
3
4
5
6
7
8
9
class MyResource {
   @JsonSerialize(using = org.sdase.commons.server.jackson.Iso8601Serializer.class)
   private ZonedDateTime zonedDateTime;

   @JsonSerialize(using = org.sdase.commons.server.jackson.Iso8601Serializer.WithMillis.class)
   private ZonedDateTime zonedDateTimeWithMillis;

   // ...
}

Resources that should be processed for HAL links must be annotated as @Resource. Links are added directly in the resource class and are annotated as @Link. Embedded resources can be added as @EmbeddedResource. The Open API Tools are used to render them in appropriate _links and _embedded properties. Links are properly documented in Swagger when io.openapitools.hal:swagger-hal is added to the dependencies. io.openapitools.hal:swagger-hal is shipped with sda-commons-server-openapi.

HAL link support may be disabled in the JacksonConfigurationBundle.builder().

Example:

1
2
3
4
5
6
7
@Resource
public class Person {
   @Link 
   private HALLink self;
   private String name;
   // ...
}
1
2
3
GET /persons/123

=> {"_links":{"self":{"href":"/persons/123"}},"name":"John Doe"}

EmbedHelper

To decide whether a resource is just linked or embedded, the EmbedHelper can be used. If query parameters for embedding are passed, like /api/cars?embed=drivers,owner, EmbedHelper.isEmbeddingOfRelationRequested(relationName) can be used to check whether a resource should be embedded:

1
2
3
4
5
6
7
EmbedHelper embedHelper = new EmbedHelper(environment);

...

if (embedHelper.isEmbeddingOfRelationRequested("owner")) {
   carResource.setOwner(createPerson(ownerId));
}

In an application that uses CDI the EmbedHelper should be instantiated the same way and provided by a producer method:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
   private EmbedHelper embedHelper;

   @Override
   public void run(Configuration config, Environment environment) {
      // ...
      this.embedHelper = new EmbedHelper(environment);
   }

   @Produces
   public EmbedHelper embedHelper() {
      return this.embedHelper;
   }

Field filtering feature for resources

The JacksonConfigurationBundle registers the FieldFilterModule to add the FieldFilterSerializerModifier. The modifier uses the JAX-RS request context to identify the query param fields. If such a query param is present, only the requested fields (comma separated) are rendered in the resource view. additionally HAL links and embedded resources are rendered as well.

Field filtering support may be disabled in the JacksonConfigurationBundle.builder().

Example:

1
2
3
4
5
6
7
@EnableFieldFilter
public class Person {
   private String firstName;
   private String surName;
   private String nickName;
   // ...
}
1
2
3
GET /persons/123?fields=firstName,nickName

=> {"firstName":"John","nickName":"Johnny"}

Configuration

Disable HAL support

The JacksonConfigurationBundle may be initialized without HAL support, if links are not needed or achieved in another way in the application:

1
JacksonConfigurationBundle.builder().withoutHalSupport().build();

Customize the ObjectMapper

Custom configurations of the ObjectMapper can be achieved by adding a customization consumer which receives the used ObjectMapper instance:

1
2
3
JacksonConfigurationBundle.builder()
    .withCustomization(om -> om.enable(SerializationFeature.INDENT_OUTPUT))
    .build();

YAML

If the JacksonYAMLProvider is available in the classpath, it will be registered to support requests that Accept application/yaml. This is especially useful for Swagger which provides the swagger.json also as swagger.yaml.

To activate YAML support, a dependency to com.fasterxml.jackson.jaxrs:jackson-jaxrs-yaml-provider has to be added. It is shipped in an appropriate version with sda-commons-server-openapi.

Error Format

Exceptions are mapped to a common error format that looks like the following example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
422 Unprocessable Entity
{
    "title": "Request parameters are not valid",
    "invalidParams": [
         {
            "field": "manufacture",
            "reason": "Audi has no Golf GTI model (not found)"
            "errorCode": "FIELD_CORRELATION_ERROR"
        },
        {
            "field": "model",
            "reason": "Golf GTI is unkown by Audi (not found)"
            "errorCode": "FIELD_CORRELATION_ERROR"
        }
    ]
}

For validation errors, the invalidParams section is filled. For other errors, just a title is given.

  • "field" defines the invalid field within the JSON structure
  • "reason" gives a hint why the value is not valid. This is the error message of the validation.
  • "errorCode" is the validation annotation given in uppercase underscore notation

The reason might be in different language due to internationalization.

Examples how exceptions and the error structure should be used, can be found within the example project sda-commons-server-errorhandling-example