Skip to content

Commit

Permalink
doc: split JSON Binding documentation into sections (#11556)
Browse files Browse the repository at this point in the history
This pull request does not modify the documentation; just split it into sections.

Individual sections make it easy to share a link to a particular section and add a cross-reference.
  • Loading branch information
sdelamo authored Jan 30, 2025
1 parent 3e2ae3f commit 444c5a1
Show file tree
Hide file tree
Showing 16 changed files with 122 additions and 146 deletions.
4 changes: 2 additions & 2 deletions src/main/docs/guide/httpServer/formData.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ To make data binding model customizations consistent between form data and JSON,

The advantage of this approach is that the same Jackson annotations used for customizing JSON binding can be used for form submissions.

In practice this means that to bind regular form data, the only change required to the previous JSON binding code is updating the api:http.MediaType[] consumed:
In practice this means that to bind regular form data, the only change required to the <<bindingUsingPOJOs, previous JSON binding code>> is updating the api:http.MediaType[] consumed:

snippet::io.micronaut.docs.server.json.PersonController[tags="class,regular,endclass", indent=0, title="Binding Form Data to POJOs"]

TIP: To avoid denial of service attacks, collection types and arrays created during binding are limited by the setting `jackson.arraySizeThreshold` in your configuration file (e.g `application.yml`)
TIP: To avoid denial-of-service attacks, collection types and arrays created during binding are limited by the setting `jackson.arraySizeThreshold` in your configuration file (e.g `application.yml`)

Alternatively, instead of using a POJO you can bind form data directly to method parameters (which works with JSON too!):

Expand Down
143 changes: 0 additions & 143 deletions src/main/docs/guide/httpServer/jsonBinding.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,146 +9,3 @@ By default, the api:http.annotation.Controller[] annotation specifies that the c
Since Micronaut Framework 4.0, users must choose how they want to serialize (Jackson Databind or Micronaut Serialization). Both approaches allow the usage of https://micronaut-projects.github.io/micronaut-serialization/latest/guide/index.html#jacksonAnnotations[Jackson Annotations].

With either approach, the Micronaut framework reads incoming JSON in a non-blocking manner.

== Serialize using Micronaut Serialization

https://micronaut-projects.github.io/micronaut-serialization/latest/guide/index.html#quickStart[Micronaut Serialization] offers reflection-free serialization using build-time <<introspection, Bean Introspections>>. It supports alternative formats such as https://micronaut-projects.github.io/micronaut-serialization/latest/guide/index.html#jsonpQuick[JSON-P or JSON-B]. You need to add the following dependencies:

dependency:micronaut-serde-processor[groupId=io.micronaut.serde,scope=annotationProcessor]
dependency:micronaut-serde-jackson[groupId=io.micronaut.serde]

== Serialization using Jackson Databind

To serialize using https://github.com/FasterXML/jackson[Jackson] Databind include the following dependency:

dependency:micronaut-jackson-databind[]

== JsonMapper

You may be used to work with https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html[Jackson's `ObjectMapper`]. However, we don't recommend using Jackson's `ObjectMapper` directly; instead you should use api:json.JsonMapper[], an API almost identical to Jackson's `ObjectMapper`. Moreover, both <<jsonBinding, Micronaut Serialization and Micronaut Jackson Databind>> implement api:json.JsonMapper[].

You can inject a bean of type `JsonMapper` or manually instantiate one via `JsonMapper.createDefault()`.

== Binding using Reactive Frameworks

From a developer perspective however, you can generally just work with Plain Old Java Objects (POJOs) and can optionally use a Reactive framework such as https://github.com/ReactiveX/RxJava[RxJava] or https://projectreactor.io[Project Reactor]. The following is an example of a controller that reads and saves an incoming POJO in a non-blocking way from JSON:

snippet::io.micronaut.docs.server.json.PersonController[tags="class,single,endclass", indent=0, title="Using Reactive Streams to Read the JSON"]

<1> The method receives a `Publisher` which emits the POJO once the JSON has been read
<2> The `map` method stores the instance in a `Map`
<3> An api:http.HttpResponse[] is returned

Using cURL from the command line, you can POST JSON to the `/people` URI:

.Using cURL to Post JSON
----
$ curl -X POST localhost:8080/people -d '{"firstName":"Fred","lastName":"Flintstone","age":45}'
----

== Binding Using CompletableFuture

The same method as the previous example can also be written with the link:{jdkapi}/java.base/java/util/concurrent/CompletableFuture.html[CompletableFuture] API instead:

snippet::io.micronaut.docs.server.json.PersonController[tags="class,future,endclass", indent=0, title="Using CompletableFuture to Read the JSON"]

The above example uses the `thenApply` method to achieve the same as the previous example.

== Binding using POJOs

Note however you can just as easily write:

snippet::io.micronaut.docs.server.json.PersonController[tags="class,regular,endclass", indent=0, title="Binding JSON POJOs"]

The Micronaut framework only executes your method once the data has been read in a non-blocking manner.

TIP: You can customize the output in various ways, such as using https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations[Jackson annotations].

== Jackson Configuration

If you use <<jsonBinding, `micronaut-jackson-databind`>>, the Jackson's `ObjectMapper` can be configured through configuration with the api:io.micronaut.jackson.JacksonConfiguration[] class.

All Jackson configuration keys start with `jackson`.

|=======
| dateFormat | String | The date format
| locale | String | Uses link:{jdkapi}/java.base/java/util/Locale.html#forLanguageTag-java.lang.String-[Locale.forLanguageTag]. Example: `en-US`
| timeZone | String |Uses link:{jdkapi}/java.base/java/util/TimeZone.html#getTimeZone-java.lang.String-[TimeZone.getTimeZone]. Example: `PST`
| serializationInclusion | String | One of link:{jackson-annotations}com/fasterxml/jackson/annotation/JsonInclude.Include.html[JsonInclude.Include]. Example: `ALWAYS`
| propertyNamingStrategy | String | Name of an instance of link:{jackson-databind}com/fasterxml/jackson/databind/PropertyNamingStrategy.html[PropertyNamingStrategy]. Example: `SNAKE_CASE`
| defaultTyping | String | The global defaultTyping for polymorphic type handling from enum link:{jackson-databind}com/fasterxml/jackson/databind/ObjectMapper.DefaultTyping.html[ObjectMapper.DefaultTyping]. Example: `NON_FINAL`
|=======

Example:

[configuration]
----
jackson:
serializationInclusion: ALWAYS
----

=== Features

If you use <<jsonBinding, `micronaut-jackson-databind`>>, all Jackson's features can be configured with their name as the key and a boolean to indicate enabled or disabled.

|======
|serialization | Map | link:{jackson-databind}com/fasterxml/jackson/databind/SerializationFeature.html[SerializationFeature]
|deserialization | Map | link:{jackson-databind}com/fasterxml/jackson/databind/DeserializationFeature.html[DeserializationFeature]
|mapper | Map | link:{jackson-databind}com/fasterxml/jackson/databind/MapperFeature.html[MapperFeature]
|parser | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonParser.Feature.html[JsonParser.Feature]
|generator | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonGenerator.Feature.html[JsonGenerator.Feature]
|factory | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonFactory.Feature.html[JsonFactory.Feature]
|======

Example:

[configuration]
----
jackson:
serialization:
indentOutput: true
writeDatesAsTimestamps: false
deserialization:
useBigIntegerForInts: true
failOnUnknownProperties: false
----

=== Further customising `JsonFactory`

If you use <<jsonBinding, `micronaut-jackson-databind`>>, there may be situations where you wish to customise the `JsonFactory` used by the `ObjectMapper` beyond the configuration of features (for example to allow custom character escaping).
This can be achieved by providing your own `JsonFactory` bean, or by providing a `BeanCreatedEventListener<JsonFactory>` which configures the default bean on startup.

=== Support for `@JsonView`

If you use <<jsonBinding, `micronaut-jackson-databind`>>, you can use the `@JsonView` annotation on controller methods if you set `jackson.json-view.enabled` to `true` in your configuration file (e.g `application.yml`).

Jackson's `@JsonView` annotation lets you control which properties are exposed on a per-response basis. See https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON Views] for more information.

=== Beans

If you use <<jsonBinding, `micronaut-jackson-databind`>>, in addition to configuration, beans can be registered to customize Jackson. All beans that extend any of the following classes are registered with the object mapper:

* link:{jackson-databind}com/fasterxml/jackson/databind/Module.html[Module]
* link:{jackson-databind}com/fasterxml/jackson/databind/JsonDeserializer.html[JsonDeserializer]
* link:{jackson-databind}com/fasterxml/jackson/databind/JsonSerializer.html[JsonSerializer]
* link:{jackson-databind}com/fasterxml/jackson/databind/KeyDeserializer.html[KeyDeserializer]
* link:{jackson-databind}com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.html[BeanDeserializerModifier]
* link:{jackson-databind}com/fasterxml/jackson/databind/ser/BeanSerializerModifier.html[BeanSerializerModifier]

=== Service Loader

Any modules registered via the service loader are also added to the default object mapper.

=== Number Precision

During JSON parsing, the framework may convert any incoming data to an intermediate object model. By default, this model uses `BigInteger`, `long` and `double` for numeric values. This means some information that could be represented by `BigDecimal` may be lost. For example, numbers with many decimal places that cannot be represented by `double` may be truncated, even if the target type for deserialization uses `BigDecimal`. Metadata on the number of trailing zeroes (`BigDecimal.precision()`), e.g. the difference between `0.12` and `0.120`, is also discarded.

If you need full accuracy for number types, use the following configuration:

[configuration]
----
jackson:
deserialization:
useBigIntegerForInts: true
useBigDecimalForFloats: true
----
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The same method as the previous example can also be written with the link:{jdkapi}/java.base/java/util/concurrent/CompletableFuture.html[CompletableFuture] API instead:

snippet::io.micronaut.docs.server.json.PersonController[tags="class,future,endclass", indent=0, title="Using CompletableFuture to Read the JSON"]

The above example uses the `thenApply` method to achieve the same as the previous example.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Note however you can just as easily write:

snippet::io.micronaut.docs.server.json.PersonController[tags="class,regular,endclass", indent=0, title="Binding JSON POJOs"]

The Micronaut framework only executes your method once the data has been read in a non-blocking manner.

TIP: You can customize the output in various ways, such as using https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations[Jackson annotations].
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
From a developer perspective however, you can generally just work with Plain Old Java Objects (POJOs) and can optionally use a Reactive framework such as https://github.com/ReactiveX/RxJava[RxJava] or https://projectreactor.io[Project Reactor]. The following is an example of a controller that reads and saves an incoming POJO in a non-blocking way from JSON:

snippet::io.micronaut.docs.server.json.PersonController[tags="class,single,endclass", indent=0, title="Using Reactive Streams to Read the JSON"]

<1> The method receives a `Publisher` which emits the POJO once the JSON has been read
<2> The `map` method stores the instance in a `Map`
<3> An api:http.HttpResponse[] is returned

Using cURL from the command line, you can POST JSON to the `/people` URI:

.Using cURL to Post JSON
----
$ curl -X POST localhost:8080/people -d '{"firstName":"Fred","lastName":"Flintstone","age":45}'
----
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
If you use <<jsonBinding, `micronaut-jackson-databind`>>, the Jackson's `ObjectMapper` can be configured through configuration with the api:io.micronaut.jackson.JacksonConfiguration[] class.

All Jackson configuration keys start with `jackson`.

|=======
| dateFormat | String | The date format
| locale | String | Uses link:{jdkapi}/java.base/java/util/Locale.html#forLanguageTag-java.lang.String-[Locale.forLanguageTag]. Example: `en-US`
| timeZone | String |Uses link:{jdkapi}/java.base/java/util/TimeZone.html#getTimeZone-java.lang.String-[TimeZone.getTimeZone]. Example: `PST`
| serializationInclusion | String | One of link:{jackson-annotations}com/fasterxml/jackson/annotation/JsonInclude.Include.html[JsonInclude.Include]. Example: `ALWAYS`
| propertyNamingStrategy | String | Name of an instance of link:{jackson-databind}com/fasterxml/jackson/databind/PropertyNamingStrategy.html[PropertyNamingStrategy]. Example: `SNAKE_CASE`
| defaultTyping | String | The global defaultTyping for polymorphic type handling from enum link:{jackson-databind}com/fasterxml/jackson/databind/ObjectMapper.DefaultTyping.html[ObjectMapper.DefaultTyping]. Example: `NON_FINAL`
|=======

Example:

[configuration]
----
jackson:
serializationInclusion: ALWAYS
----
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
If you use <<jsonBinding, `micronaut-jackson-databind`>>, in addition to configuration, beans can be registered to customize Jackson. All beans that extend any of the following classes are registered with the object mapper:

* link:{jackson-databind}com/fasterxml/jackson/databind/Module.html[Module]
* link:{jackson-databind}com/fasterxml/jackson/databind/JsonDeserializer.html[JsonDeserializer]
* link:{jackson-databind}com/fasterxml/jackson/databind/JsonSerializer.html[JsonSerializer]
* link:{jackson-databind}com/fasterxml/jackson/databind/KeyDeserializer.html[KeyDeserializer]
* link:{jackson-databind}com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.html[BeanDeserializerModifier]
* link:{jackson-databind}com/fasterxml/jackson/databind/ser/BeanSerializerModifier.html[BeanSerializerModifier]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
If you use <<jsonBinding, `micronaut-jackson-databind`>>, all Jackson's features can be configured with their name as the key and a boolean to indicate enabled or disabled.

|======
|serialization | Map | link:{jackson-databind}com/fasterxml/jackson/databind/SerializationFeature.html[SerializationFeature]
|deserialization | Map | link:{jackson-databind}com/fasterxml/jackson/databind/DeserializationFeature.html[DeserializationFeature]
|mapper | Map | link:{jackson-databind}com/fasterxml/jackson/databind/MapperFeature.html[MapperFeature]
|parser | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonParser.Feature.html[JsonParser.Feature]
|generator | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonGenerator.Feature.html[JsonGenerator.Feature]
|factory | Map | link:{jackson-core}com/fasterxml/jackson/core/JsonFactory.Feature.html[JsonFactory.Feature]
|======

Example:

[configuration]
----
jackson:
serialization:
indentOutput: true
writeDatesAsTimestamps: false
deserialization:
useBigIntegerForInts: true
failOnUnknownProperties: false
----
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
If you use <<jsonBinding, `micronaut-jackson-databind`>>, there may be situations where you wish to customise the `JsonFactory` used by the `ObjectMapper` beyond the configuration of features (for example to allow custom character escaping).
This can be achieved by providing your own `JsonFactory` bean, or by providing a `BeanCreatedEventListener<JsonFactory>` which configures the default bean on startup.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
During JSON parsing, the framework may convert any incoming data to an intermediate object model. By default, this model uses `BigInteger`, `long` and `double` for numeric values. This means some information that could be represented by `BigDecimal` may be lost. For example, numbers with many decimal places that cannot be represented by `double` may be truncated, even if the target type for deserialization uses `BigDecimal`. Metadata on the number of trailing zeroes (`BigDecimal.precision()`), e.g. the difference between `0.12` and `0.120`, is also discarded.

If you need full accuracy for number types, use the following configuration:

[configuration]
----
jackson:
deserialization:
useBigIntegerForInts: true
useBigDecimalForFloats: true
----
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Any modules registered via the service loader are also added to the default object mapper.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
If you use <<jsonBinding, `micronaut-jackson-databind`>>, you can use the `@JsonView` annotation on controller methods if you set `jackson.json-view.enabled` to `true` in your configuration file (e.g `application.yml`).

Jackson's `@JsonView` annotation lets you control which properties are exposed on a per-response basis. See https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON Views] for more information.
3 changes: 3 additions & 0 deletions src/main/docs/guide/httpServer/jsonBinding/jsonMapper.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
You may be used to work with https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html[Jackson's `ObjectMapper`]. However, we don't recommend using Jackson's `ObjectMapper` directly; instead you should use api:json.JsonMapper[], an API almost identical to Jackson's `ObjectMapper`. Moreover, both <<jsonBinding, Micronaut Serialization and Micronaut Jackson Databind>> implement api:json.JsonMapper[].

You can inject a bean of type `JsonMapper` or manually instantiate one via `JsonMapper.createDefault()`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
To serialize using https://github.com/FasterXML/jackson[Jackson] Databind include the following dependency:

dependency:micronaut-jackson-databind[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
https://micronaut-projects.github.io/micronaut-serialization/latest/guide/index.html#quickStart[Micronaut Serialization] offers reflection-free serialization using build-time <<introspection, Bean Introspections>>. It supports alternative formats such as https://micronaut-projects.github.io/micronaut-serialization/latest/guide/index.html#jsonpQuick[JSON-P or JSON-B]. You need to add the following dependencies:

dependency:micronaut-serde-processor[groupId=io.micronaut.serde,scope=annotationProcessor]
dependency:micronaut-serde-jackson[groupId=io.micronaut.serde]
17 changes: 16 additions & 1 deletion src/main/docs/guide/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,22 @@ httpServer:
title: Reactive HTTP Request Processing
bodyAnnotation: Using the @Body Annotation
reactiveResponses: Reactive Responses
jsonBinding: JSON Binding
jsonBinding:
title: JSON Binding
serializeUsingMicronautSerialization: Serialize using Micronaut Serialization
serializationUsingJacksonDatabind: Serialization using Jackson Databind
jsonMapper: JsonMapper
bindingUsingReactiveFrameworks: Binding using Reactive Frameworks
bindingUsingCompletableFuture: Binding Using CompletableFuture
bindingUsingPOJOs: Binding using POJOs
jacksonConfiguration:
title: Jackson Configuration
jacksonConfigurationFeatures: Features
jacksonConfigurationFurtherCustomisingJsonFactory: Further customising `JsonFactory`
jacksonConfigurationSupportForJsonView: Support for `@JsonView`
jacksonConfigurationBeans: Beans
jacksonConfigurationServiceLoader: Service Loader
jacksonConfigurationNumberPrecision: Number Precision
plainTextResponses: Plain Text Responses
datavalidation:
title: Data Validation
Expand Down

0 comments on commit 444c5a1

Please sign in to comment.