Json
Module allows you to create productive and reflection-free JSON readers and writers for application classes using annotations.
Dependency¶
Dependency build.gradle
:
Module:
Dependency build.gradle.kts
:
Module:
Writer¶
You can use @JsonWriter
to create a writer only:
Reader¶
You can use @JsonReader
to create a reader only:
Reader & Writer¶
You can use @Json
to create a reader and a writer at once.
In most cases, it is the @Json
annotation that is preferred:
Required fields¶
By default, all fields declared in an object are considered required (NotNull).
By default, all fields declared in an object that do not use the Kotlin Nullability syntax are considered required (NotNull).
Optional fields¶
In case a field in Json is optional, that is, it may not exist then,
you can use the @Nullable
annotation to match the field in Json and DTO:
- Any
@Nullable
annotation will do, such asjavax.annotation.Nullable
/jakarta.annotation.Nullable
/org.jetbrains.annotations.Nullable
/ etc.
It is expected to use the Kotlin Nullability syntax and mark such a parameter as Nullable:
Field naming¶
In case a field in Json is named differently from what you want to use in a class,
you can use the @JsonField
annotation to match the field in Json and the DTO.
Field ignore¶
In case you don't want to read/write a field in DTO,
you can use the @JsonSkip
annotation and ignore such a field.
Serialization levels¶
The default behavior is not to write fields with null
values. (1)
IncludeType.NON_NULL
- include the field in the record if notnull
.
In case you want to change the behavior of the record in these moments, it is suggested to use the @JsonInclude
annotation.
The annotation can be used not only over a field, but also over a class and then the rule will apply to all fields at once.
Various use cases are available:
IncludeType.ALWAYS
- include the field in the record alwaysIncludeType.NON_NULL
- include the field in the record if it is notnull
.IncludeType.NON_EMPTY
- include the field in the record if it is notnull
and not an empty collection
Example of annotation usage:
Serialization constructor¶
If you want to use a specific constructor for serialization,
it can be done by specifying the @JsonReader
annotation above the constructor or the lower-priority @Json
annotation:
JsonNullable wrapper¶
In case you want to distinguish a missing field from a specified null
value during deserialization,
it is supposed to use a special type JsonNullable
, which allows interpreting all states of the field.
Sealed classes and interfaces¶
In case you need to write different Json objects depending on the value in a particular field, you are supposed to use an isolated class/interface to represent such objects.
Two annotations are added to support isolated classes:
@JsonDiscriminatorField
- specifies the discriminator field in the DTO with which the sealed class/interface is tagged@JsonDiscriminatorValue
- the value for the above field, marks the inheritor class of the sealed class/interface
@Json
@JsonDiscriminatorField("type")
public sealed interface Event {
@JsonDiscriminatorValue("firstType")
record FirstTypeEvent(String id, FirstData data) implements Event {}
@JsonDiscriminatorValue("secondType")
record SecondTypeEvent(String id, SecondData data) implements Event {}
@JsonDiscriminatorValue("thirdType")
record ThirdTypeEvent(String id, ThirdData data) implements Event {}
}
@Json
@JsonDiscriminatorField("type")
sealed interface Event {
@JsonDiscriminatorValue("firstType")
data class FirstTypeEvent(val id: String, data: FirstData) : Event
@JsonDiscriminatorValue("secondType")
data class SecondTypeEvent(val id: String, data: SecondData) : Event
@JsonDiscriminatorValue("thirdType")
data class ThirdTypeEvent(val id: String, data: ThirdData) : Event
}
A JsonReader
and JsonWriter
will be created for the inheritor classes using the same rules as if they had the @Json
annotation on them and a JsonReader
and JsonWriter
will be created for the sealed class/interface itself.
The Json object below will be written to the FirstTypeEvent
class:
Supported types¶
Module provides an extensive list of supported out-of-the-box types that cover most of what you might need.
List of supported types
- Boolean
- boolean
- Short
- short
- Integer
- int
- Long
- long
- Double
- double
- Float
- float
- byte[]
- String
- UUID
- BigInteger
- BigDecimal
- List
- Set
- LocalDate
- LocalTime
- LocalDateTime
- OffsetTime
- OffsetDateTime
- ZonedDateTime
- Year
- YearMonth
- MonthDay
- Month
- DayOfWeek
- ZoneId
- Duration
Custom types¶
In case you need to write/read your custom type, it is suggested to register your custom factory for JsonReader
/ JsonWriter
:
Example of registering a JsonWriter
:
Jackson¶
In case one wants to use Jackson
for writing/reading, one can register factory that
provide ObjectMapper
and the corresponding Mappers
that are required in other Kora modules will be provided by the dependency below:
Dependency build.gradle
:
annotationProcessor "ru.tinkoff.kora:json-annotation-processor"
implementation "ru.tinkoff.kora:jackson-module"
Module:
Dependency build.gradle.kts
:
Module: