FasterXML / jackson-module-kotlin

Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes.
Apache License 2.0
1.12k stars 175 forks source link

Performance improvement of `strictNullCheck` #719

Open k163377 opened 11 months ago

k163377 commented 11 months ago

Use case

Currently, if the strictNullChecks option is enabled, the deserialization performance of collections is significantly degraded. It also reduces the performance of all deserialization, although very slightly.

The following graphs show actual benchmark results. image https://github.com/ProjectMapK/kogera-benchmark#original

In jackson-module-kotlin, we are looking for ways to mitigate this performance degradation.

Describe the solution you'd like

Currently, the following two methods have been found

As is common with both methods, exceptions and error messages will change.

Check with Converter

The first is to use the following Converter to perform null checks. https://github.com/ProjectMapK/jackson-module-kogera/blob/b9aca60a58b8a6b135150e8921229d38699475a9/src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/Converters.kt#L13-L69

This method has the problem of limiting the information that can be displayed in error messages, but has almost no other restrictions. In the Kogera experimental implementation, the following error messages are displayed. At least the properties in error are readable.

package io.github.projectmapk.jackson.module.kogera._integration.deser

// deser target
class ArrayWrapper(val value: Array<Int>)
com.fasterxml.jackson.databind.exc.MismatchedInputException: A null value was entered for the parameter value. (through reference chain: io.github.projectmapk.jackson.module.kogera._integration.deser.StrictNullChecksTest$ArrayWrapper["value"])

The following graph shows the results of a similar benchmark run on Kogera. You can see that the performance degradation of the deserialization of the collection is smaller. image https://github.com/ProjectMapK/kogera-benchmark#kogera

Override SetterInfo.contentNulls

The second is to override SetterInfo.contentNulls. This has the same behavior as specifying JsonSetter(contentNulls = Nulls.FAIL).

A possible way to achieve this would be to implement AnnotationIntrospector.findSetterInfo. At this time I am not aware of any other method. https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/latest/com/fasterxml/jackson/databind/AnnotationIntrospector.html#findSetterInfo-com.fasterxml.jackson.databind.introspect.Annotated-

The biggest advantage of this approach is that the performance is theoretically the best (although this has not yet been confirmed by benchmarking). Another advantage is that the implementation in kotlin-module is minimal.

On the other hand, there are some disadvantages with the current possible implementation.

First, it interacts with other AnnotationIntrospectors. This can be a problem if the contentNulls are custom, since the AnnotationIntrospector is constrained to apply only one of the results(If JsonSetter is explicitly specified, it will be used).

Second, the kotlin-module will no longer customize error messages. The error message for the ArrayWrapper just described is as follows

com.fasterxml.jackson.databind.exc.InvalidNullException: Invalid `null` value encountered for property "value"
 at [Source: (String)"{"value":[null]}"; line: 1, column: 11] (through reference chain: io.github.projectmapk.jackson.module.kogera._integration.deser.StrictNullChecksTest$ArrayWrapper["value"]->java.lang.Object[][0])

Describe alternatives you've considered

No response

Additional context

No response