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):
null
to a non-nullable variable (e.g.String
) if variable has default value, use it. otherwise throw exceptionnull
to a nullable variable (e,g, ‘String?’) set tonull
regardless if there is a default value or not (as the developer has explicitly declared thatnull
is acceptable).not a good solution as many developers are using 3rd party APIs which they have no control over
Branch 2.10 now allows
null
to 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
Int
field, regardless of whether or not it has a default value, will be set to0
if the parsed JSON field is null.I think, if the
Int
field is non-null, then an exception is more appropriate, as null and zero are not the same thing. Perhaps theMissingKotlinParameterException
should 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 inputnull
with the default value if a default value is defined.I think that in some cases,
null
is 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
Boolean
value. Since Kotlin’sBoolean
is 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 throwMissingKotlinParameterException
if 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 ?: 0
in my code. This way, I can default to1
or-1
if 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
name
is null, then “default value” would be assignedname
how can i go about achieving this?
In my travels, I have discovered that enabling
DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES
will give aMissingKotlinParameterException
if 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
AnnotationIntrospector
to 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_PRIMITIVES
and 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).