Skip to content

Commit cc06473

Browse files
committed
Fixed handling of nullToEmpty options
Fixed the problem that the nullToEmpty options overrides the specification by annotation and the nullToEmpty options affects other than parameters. This fixes #922 completely. Also, considerations regarding JsonProperty.isRequired have been added.
1 parent f9cf1a3 commit cc06473

File tree

1 file changed

+35
-44
lines changed

1 file changed

+35
-44
lines changed

src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt

+35-44
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.fasterxml.jackson.module.kotlin
22

33
import com.fasterxml.jackson.annotation.JsonProperty
4+
import com.fasterxml.jackson.annotation.OptBoolean
45
import com.fasterxml.jackson.databind.DeserializationFeature
56
import com.fasterxml.jackson.databind.JsonSerializer
67
import com.fasterxml.jackson.databind.Module
@@ -36,20 +37,27 @@ internal class KotlinAnnotationIntrospector(
3637
// TODO: implement nullIsSameAsDefault flag, which represents when TRUE that if something has a default value, it can be passed a null to default it
3738
// this likely impacts this class to be accurate about what COULD be considered required
3839

40+
// If a new isRequired is explicitly specified or the old required is true, those values take precedence.
41+
// In other cases, override is done by KotlinModule.
42+
private fun JsonProperty.forceRequiredByAnnotation(): Boolean? = when {
43+
isRequired != OptBoolean.DEFAULT -> isRequired.asBoolean()
44+
required -> true
45+
else -> null
46+
}
47+
48+
private fun AccessibleObject.forceRequiredByAnnotation(): Boolean? =
49+
getAnnotation(JsonProperty::class.java)?.forceRequiredByAnnotation()
50+
3951
override fun hasRequiredMarker(
4052
m: AnnotatedMember
4153
): Boolean? = m.takeIf { it.member.declaringClass.isKotlinClass() }?.let { _ ->
4254
cache.javaMemberIsRequired(m) {
4355
try {
44-
when {
45-
nullToEmptyCollection && m.type.isCollectionLikeType -> false
46-
nullToEmptyMap && m.type.isMapLikeType -> false
47-
else -> when (m) {
48-
is AnnotatedField -> m.hasRequiredMarker()
49-
is AnnotatedMethod -> m.hasRequiredMarker()
50-
is AnnotatedParameter -> m.hasRequiredMarker()
51-
else -> null
52-
}
56+
when (m) {
57+
is AnnotatedField -> m.hasRequiredMarker()
58+
is AnnotatedMethod -> m.hasRequiredMarker()
59+
is AnnotatedParameter -> m.hasRequiredMarker()
60+
else -> null
5361
}
5462
} catch (_: UnsupportedOperationException) {
5563
null
@@ -100,28 +108,9 @@ internal class KotlinAnnotationIntrospector(
100108
}
101109

102110
private fun AnnotatedField.hasRequiredMarker(): Boolean? {
103-
val byAnnotation = (member as Field).isRequiredByAnnotation()
104-
val byNullability = (member as Field).kotlinProperty?.returnType?.isRequired()
105-
106-
return requiredAnnotationOrNullability(byAnnotation, byNullability)
107-
}
108-
109-
private fun AccessibleObject.isRequiredByAnnotation(): Boolean? = annotations
110-
.firstOrNull { it.annotationClass == JsonProperty::class }
111-
?.let { it as JsonProperty }
112-
?.required
113-
114-
private fun requiredAnnotationOrNullability(byAnnotation: Boolean?, byNullability: Boolean?): Boolean? {
115-
if (byAnnotation != null && byNullability != null) {
116-
return byAnnotation || byNullability
117-
} else if (byNullability != null) {
118-
return byNullability
119-
}
120-
return byAnnotation
121-
}
122-
123-
private fun Method.isRequiredByAnnotation(): Boolean? {
124-
return (this.annotations.firstOrNull { it.annotationClass.java == JsonProperty::class.java } as? JsonProperty)?.required
111+
val field = member as Field
112+
return field.forceRequiredByAnnotation()
113+
?: field.kotlinProperty?.returnType?.isRequired()
125114
}
126115

127116
// Since Kotlin's property has the same Type for each field, getter, and setter,
@@ -136,33 +125,35 @@ internal class KotlinAnnotationIntrospector(
136125
private fun AnnotatedMethod.getRequiredMarkerFromCorrespondingAccessor(): Boolean? {
137126
member.declaringClass.kotlin.declaredMemberProperties.forEach { kProperty ->
138127
if (kProperty.javaGetter == this.member || (kProperty as? KMutableProperty1)?.javaSetter == this.member) {
139-
val byAnnotation = this.member.isRequiredByAnnotation()
140-
val byNullability = kProperty.isRequiredByNullability()
141-
return requiredAnnotationOrNullability(byAnnotation, byNullability)
128+
return member.forceRequiredByAnnotation() ?: kProperty.isRequiredByNullability()
142129
}
143130
}
144131
return null
145132
}
146133

147134
// Is the member method a regular method of the data class or
148135
private fun Method.getRequiredMarkerFromAccessorLikeMethod(): Boolean? = cache.kotlinFromJava(this)?.let { func ->
149-
val byAnnotation = this.isRequiredByAnnotation()
150-
return when {
151-
func.isGetterLike() -> requiredAnnotationOrNullability(byAnnotation, func.returnType.isRequired())
152-
func.isSetterLike() -> requiredAnnotationOrNullability(byAnnotation, func.valueParameters[0].isRequired())
136+
forceRequiredByAnnotation() ?: when {
137+
func.isGetterLike() -> func.returnType.isRequired()
138+
// If nullToEmpty could be supported for setters,
139+
// a branch similar to AnnotatedParameter.hasRequiredMarker should be added.
140+
func.isSetterLike() -> func.valueParameters[0].isRequired()
153141
else -> null
154142
}
155143
}
156144

157145
private fun KFunction<*>.isGetterLike(): Boolean = parameters.size == 1
158146
private fun KFunction<*>.isSetterLike(): Boolean = parameters.size == 2 && returnType == UNIT_TYPE
159147

160-
private fun AnnotatedParameter.hasRequiredMarker(): Boolean? {
161-
val byAnnotation = this.getAnnotation(JsonProperty::class.java)?.required
162-
val byNullability = cache.findKotlinParameter(this)?.isRequired()
163-
164-
return requiredAnnotationOrNullability(byAnnotation, byNullability)
165-
}
148+
private fun AnnotatedParameter.hasRequiredMarker(): Boolean? = getAnnotation(JsonProperty::class.java)
149+
?.forceRequiredByAnnotation()
150+
?: run {
151+
when {
152+
nullToEmptyCollection && type.isCollectionLikeType -> false
153+
nullToEmptyMap && type.isMapLikeType -> false
154+
else -> cache.findKotlinParameter(this)?.isRequired()
155+
}
156+
}
166157

167158
private fun AnnotatedMethod.findValueClassReturnType() = cache.findValueClassReturnType(this)
168159

0 commit comments

Comments
 (0)