jackson-module-kotlin: Using Kotlin Default Parameter Values when JSON value is null and Kotlin parameter type is Non-Nullable
I’ve got the following simplified JSON example, which I’m trying to decode into the simplified Kotlin Data Class below.
{
"boolField": null,
"stringField": null
}
data class TestObject(
val boolField: Boolean = true,
val stringField: String = "default"
)
The key thing here is that the Kotlin properties are not nullable, but there is a known default value for them. However, the JSON sometimes contains null for those fields.
I am trying to get the example JSON to decode using the default values in place of the nulls since the type is non-nullable. However, this doesn’t work out of the box, instead throwing a MissingKotlinParameterException.
I had a look at modifying the code with a feature flag to behave the way I wanted. This was easy enough to do with some minor alterations to createFromObjectWith() in KotlinValueInstantiator for the String case. However, for the Boolean case it does not work, as in Java, that non-optional Boolean becomes a boolean primitive type, which cannot take null and thus Jackson Data Binding sets it with the default value of false.
So, assuming I haven’t missed the point completely with this, I’m wondering if there’s a way in the KotlinValueInstantiator to know that the primitive types were set with their default values by Jackson Data Binding in order to make this work for primitive types too?
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 27
- Comments: 40 (13 by maintainers)
Links to this issue
Commits related to this issue
- Fixed issue #130 Default Parameter Values when JSON value is null (#259) — committed to FasterXML/jackson-module-kotlin by NumezmaT 5 years ago
- Fixed issue #130 Default Parameter Values when JSON value is null (#259) — committed to FasterXML/jackson-module-kotlin by NumezmaT 5 years ago
- Add release notes for #130 — committed to FasterXML/jackson-module-kotlin by cowtowncoder 5 years ago
expected behavior (at least for me):
nullto a non-nullable variable (e.g.String) if variable has default value, use it. otherwise throw exceptionnullto a nullable variable (e,g, ‘String?’) set tonullregardless if there is a default value or not (as the developer has explicitly declared thatnullis acceptable).not a good solution as many developers are using 3rd party APIs which they have no control over
Branch 2.10 now allows
nullto be treated as “use the default” value, but requires that you set a flag when creating the Kotlin module, otherwise the behavior stays as it was as the default behavior.I’ve also noticed that, an
Intfield, regardless of whether or not it has a default value, will be set to0if the parsed JSON field is null.I think, if the
Intfield is non-null, then an exception is more appropriate, as null and zero are not the same thing. Perhaps theMissingKotlinParameterExceptionshould be the default behaviour, and some kind of annotation could set an option to allow either converting nulls to 0, false, “” etc, or to allow replacing the inputnullwith the default value if a default value is defined.I think that in some cases,
nullis a perfectly valid value to use instead of the default value, and the absence of a field in the JSON we are deserialising is the only time where the default value always makes sense.I’m facing this issue in its more basic form. I have a non nullable no default value Kotlin
Booleanvalue. Since Kotlin’sBooleanis an alias of the primitive, likeInt, the module doesn’t require the value in the JSON input.I totally aggree with both @crypticmind and @gerob311. If I write a Kotlin data class this way :
data class TestObject( val myInteger: Int )i need myInteger to be present, not default to 0. It should throwMissingKotlinParameterExceptionif absent.If i wanted myInteger to default to 0 in case of missing, i would have written :
data class TestObject( val myInteger: Int? )and then latertestObject.myInteger ?: 0in my code. This way, I can default to1or-1if I preferPlease remove
|| paramDef.isPrimitive()here : https://github.com/FasterXML/jackson-module-kotlin/blob/c8d88c664731f31cb2cc00ed08f9d6b98197a8a9/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt#L45also curious about this. i am using a third party REST api which i have no control over and theoretically everything could be null… i have 2 possible solutions/workarounds in mind but neither of them is good enough…
the problem with this solution is a lot of boilerplate!. i gave a very simple example with only 1 variable, but in reality there are many more…
obviously the problem with this is that its kind of stupid to make every single variable nullable, expecially given kotlins null-safety “feature”. also this would lead to a lot of null-handling everytime i want to access a variable
ideally i would have this:
and if in the json i get from the server
nameis null, then “default value” would be assignednamehow can i go about achieving this?
In my travels, I have discovered that enabling
DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVESwill give aMissingKotlinParameterExceptionif the input value is null. I think this isn’t a bad workaround, or maybe this is what was always intended in this module?I’ve also discovered a slightly related case, which I’m not sure if it should be a separate ticket or not.
If I declare something like
class TestObject(val timesOfDay: List<LocalTime>), Jackson allows null values to be deserialised into the list. For example, there are no exceptions if I deserialise{"timesOfDay": ["08:00", null, "09:00"]}. I would expect this should only be allowed if I declaredclass TestObject(val timesOfDay: List<LocalTime?>). Though, I’m not sure if type erasure means that this is impossible to fix.It would be possible for Kotlin-module-provided
AnnotationIntrospectorto indicate that “null setter” logic was “use empty”. But someone has to provide the PR to do that, including tests. Asking or demanding that someone fix this will not help if no one is working on it. I do not work on Kotlin module since I don’t have enough Kotlin knowledge and since there is plenty of other necessary work (including support for modules lie Kotlin). I can help get things merged, answer questions on databind core logic. But not do fixes here.I am also facing the same issue with deserialization of csv data containing null/empty values. Is there any update on this issue ?
On
2.13.0, I met the same issue with @alturkovic. It seems the feature doesn’t still work when using@cowtowncoder That does not work with Kotlin’s data classes. I see an error being thrown saying a non-null value is being assigned to null.
I tried with
DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVESand it works as expected. I’m using Jackson 2.8.0 and jackson-module-kotlin 2.9.4.1 (I don’t know if these should match).