Usage of Jackson @JsonProperty annotation for kotlin data classes
JavaJsonJacksonKotlinDeserializationJava Problem Overview
kotlin 1.2.10 jackson-module-kotlin:2.9.0
I have the following data class in kotlin:
data class CurrencyInfo(
@JsonProperty("currency_info") var currencyInfo: CurrencyInfoItem?
)
@JsonInclude(JsonInclude.Include.NON_NULL)
data class CurrencyInfoItem(
@JsonProperty("iso_4217") var iso4217: String?,
@JsonProperty("name") var name: String?,
@JsonProperty("name_major") var nameMajor: String?,
@JsonProperty("name_minor") var nameMinor: String?,
@JsonProperty("i_ma_currency") var iMaCurrency: Int?,
@JsonProperty("i_merchant_account") var iMerchantAccount: Int?,
@JsonProperty("i_x_rate_source") var iXRateSource: Int?,
@JsonProperty("base_units") var baseUnits: Double?,
@JsonProperty("min_allowed_payment") var minAllowedPayment: Int?,
@JsonProperty("decimal_digits") var decimalDigits: Int?,
@JsonProperty("is_used") var isUsed: Boolean?
)
When I try to deserialize this data class I get the following:
{"currency_info":{"iso_4217":"CAD","name":"Canadian Dollar","imerchantAccount":0,"ixrateSource":2}}
As you can see, the last two options were deserialized incorrectly. This issue could be solved by adding directly annotation to getter @get:JsonProperty. However, according to jackson docs @JsonProperty should be assigned to getters/setters/fields
So, I want to ask is there a reliable way to annotate property for jackson in kotlin to have correct serialization/deserialization (moreover all my data classes are autogenerated, so it would be hard to create some two/three lines annotations, separately for getter and setter)
Otherwise, could this issue be resolved by some jackson settings?
According to answers below, the following works for me
private val mapper = ObjectMapper().registerKotlinModule()
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.NONE)
.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE)
Java Solutions
Solution 1 - Java
@JsonProperty
annotations in your code are all put on private fields within your data class and by default Jackson doesn't scan private fields for annotations. You have to instruct it to do otherwise by putting @JsonAutoDetect
annotation:
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
data class CurrencyInfo(
@JsonProperty("currency_info") var currencyInfo: CurrencyInfoItem?
)
or alternatively you can move your annotations on accessor methods:
data class CurrencyInfo(
@get:JsonProperty("currency_info") var currencyInfo: CurrencyInfoItem?
)
Solution 2 - Java
You can do something like this:
data class CurrencyInfo @JsonCreator constructor (
@param:JsonProperty("currency_info")
@get:JsonProperty("currency_info")
val currencyInfo: CurrencyInfoItem?
)
code above translates to java as:
public final class CurrencyInfo {
@Nullable
private final String currencyInfo;
@JsonProperty("currency_info")
@Nullable
public final String getCurrencyInfo() {
return this.currencyInfo;
}
@JsonCreator
public CurrencyInfo(@JsonProperty("currency_info") @Nullable String currencyInfo) {
this.currencyInfo = currencyInfo;
}
}
code from accepted answer translates to java as following:
First (is not pure immutable):
@JsonAutoDetect(
fieldVisibility = Visibility.ANY
)
public final class CurrencyInfo {
@Nullable
private String currencyInfo;
@Nullable
public final String getCurrencyInfo() {
return this.currencyInfo;
}
public final void setCurrencyInfo(@Nullable String var1) {
this.currencyInfo = var1;
}
public CurrencyInfo(@JsonProperty("currency_info") @Nullable String currencyInfo) {
this.currencyInfo = currencyInfo;
}
}
Second (probably has problems with deserialization):
public final class CurrencyInfo {
@Nullable
private final String currencyInfo;
@JsonProperty("currency_info")
@Nullable
public final String getCurrencyInfo() {
return this.currencyInfo;
}
public CurrencyInfo(@Nullable String currencyInfo) {
this.currencyInfo = currencyInfo;
}
}
Solution 3 - Java
You can add the jackson-module-kotlin
(https://github.com/FasterXML/jackson-module-kotlin) and register the kotlin module with jackson:
val mapper = ObjectMapper().registerKotlinModule()
Then it (and many other things) works automagically.
Solution 4 - Java
You can configure the ObjectMapper from the jackson library by calling the method setPropertyNamingStrategy(...)
Using PropertyNamingStrategy.SNAKE_CASE
should resolve your problem
See also the other available strategies here : PropertyNamingStrategy
Solution 5 - Java
Kotlin doesn't support @param and @get annotations as one annotation, so we have to write such code:
data class User(
@param:JsonProperty("id") @get:JsonProperty("id") val id: Int,
@param:JsonProperty("name") @get:JsonProperty("name") val name: String
)
Here you can tell JetBrain guys to support this feature and allow:
data class User(
@JsonProperty("id") val id: Int,
@JsonProperty("name") val name: String
)
Solution 6 - Java
this issue is resolved in
https://github.com/FasterXML/jackson-module-kotlin/issues/237
or you can also use
data class SignRequest
@param:JsonProperty("estamp_request")
@get:JsonProperty("estamp_request")
val eStamp: EstampRequest?
}
data class EstampRequest(
val tags: Map<String,Int>
)
Solution 7 - Java
I faced this problem today and what I did was register KotlinModule() in my ObjectMapper(). Bellows follow an example.
@Bean fun objectMapper = ObjectMapper().registreModule(KotlinModule())
Above it's a dummy objectMapper, I believe that you should put other configurations in your objectMapper like serializers and so on