SDA Commons Server Jackson¶
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 | |
Date and Time formatting¶
It is strongly recommended to use
LocalDatefor dates without timeZonedDateTimefor date and timesDurationfor durations with time resolutionPeriodfor 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 | |
Adding HAL links to resources¶
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 | |
1 2 3 | |
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 | |
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 | |
Field filtering feature for resources¶
The JacksonConfigurationBundle registers the JacksonFieldFilterModule, which adds the
FieldFilterSerializerModifier. The modifier reads the fields query parameter from the JAX-RS request
context. If fields is present, only requested fields are rendered. Field names can be comma-separated or
provided through repeated query parameters. Whitespace is ignored.
Field filtering is enabled per serialized type with @EnableFieldFilter. If no fields parameter is
provided, the complete response is rendered. HAL links and embedded resources are rendered regardless of
the requested fields.
Field filtering support may be disabled in the JacksonConfigurationBundle.builder().
Response fixtures below omit HAL _links; field-filtering tests still verify links remain present.
Example model:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
To enable field filtering for a resource, annotate the serialized type with @EnableFieldFilter.
1 | |
1 2 3 4 | |
Nested fields¶
Nested paths use dot notation. Nested filtering is configured per annotated type. A nested type must
enable enableNestedPathFiltering to filter its own properties; parent and child settings are independent:
1 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
By default, enableNestedPathFiltering is false. Once a parent property is selected, its complete subtree is
kept. For example:
1 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
If a nested object is not annotated with @EnableFieldFilter, selecting one of its sub-fields keeps the
complete nested object. The enableNestedPathFiltering setting is evaluated per annotated type.
1 | |
1 2 3 4 5 6 | |
The same rule applies to unannotated list items:
1 | |
1 2 3 4 5 6 7 8 | |
Nested filtering on maps¶
Requesting attributes.name filters each map value. Map keys are not path segments. To filter
fields inside map values, nested filtering must be enabled on the serialized container type and on
the map value type with @EnableFieldFilter(enableNestedPathFiltering = true). If either type does
not enable nested path filtering, the complete map value is kept.
1 | |
1 2 3 4 5 6 7 8 9 10 | |
Requesting parent map keeps complete values:
1 | |
1 2 3 4 5 6 7 8 9 10 11 12 | |
Nested filtering on lists¶
The same rules apply to list items. Requesting a nested path filters each item in the list. To
filter fields inside list items, nested filtering must be enabled on the serialized parent type and
on the list item type with @EnableFieldFilter(enableNestedPathFiltering = true). If either type
does not enable nested path filtering, the complete list item is kept.
1 | |
1 2 3 4 5 6 7 | |
Requesting the parent list keeps the complete subtree of each item.
1 | |
1 2 3 4 5 6 7 8 9 | |
HAL links remain included; omitted from fixtures for stable documentation output.
Wrapped lists¶
For wrapped lists, include wrapper property in field path. Wrapper and item types must be annotated when nested item filtering is enabled.
1 | |
1 2 3 4 5 6 7 8 | |
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 | |
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 | |
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 | |
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