Skip to content

Commit e760c59

Browse files
committed
Added consideration of JsonProperty.isRequired
from FasterXML/jackson-module-kotlin#929
1 parent 3e36a65 commit e760c59

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotationIntrospector/KotlinPrimaryAnnotationIntrospector.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.github.projectmapk.jackson.module.kogera.annotationIntrospector
22

3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
import com.fasterxml.jackson.annotation.OptBoolean
35
import com.fasterxml.jackson.databind.JavaType
46
import com.fasterxml.jackson.databind.introspect.Annotated
57
import com.fasterxml.jackson.databind.introspect.AnnotatedField
@@ -26,13 +28,22 @@ internal class KotlinPrimaryAnnotationIntrospector(
2628
private val nullToEmptyMap: Boolean,
2729
private val cache: ReflectionCache
2830
) : NopAnnotationIntrospector() {
31+
// If a new isRequired is explicitly specified or the old required is true, those values take precedence.
32+
// In other cases, override is done by KotlinModule.
33+
private fun JsonProperty.forceRequiredByAnnotation(): Boolean? = when {
34+
isRequired != OptBoolean.DEFAULT -> isRequired.asBoolean()
35+
required -> true
36+
else -> null
37+
}
38+
2939
// If JsonProperty.required is true, the behavior is clearly specified and the result is paramount.
3040
// Otherwise, the required is determined from the configuration and the definition on Kotlin.
3141
override fun hasRequiredMarker(m: AnnotatedMember): Boolean? {
3242
return cache.getJmClass(m.member.declaringClass)?.let { jmClass ->
3343
// To avoid overwriting processing by other modules, annotations are checked after JmClass has been obtained
3444
_findAnnotation(m, JSON_PROPERTY_CLASS)
35-
?.let { if (it.required) return true }
45+
?.forceRequiredByAnnotation()
46+
?.let { return it }
3647

3748
when (m) {
3849
is AnnotatedField -> m.hasRequiredMarker(jmClass)

src/test/kotlin/io/github/projectmapk/jackson/module/kogera/zPorted/test/github/GitHub922.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package io.github.projectmapk.jackson.module.kogera.zPorted.test.github
22

3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
import com.fasterxml.jackson.annotation.OptBoolean
35
import com.fasterxml.jackson.databind.BeanDescription
46
import com.fasterxml.jackson.databind.ObjectMapper
57
import io.github.projectmapk.jackson.module.kogera.KotlinFeature
8+
import io.github.projectmapk.jackson.module.kogera.defaultMapper
69
import io.github.projectmapk.jackson.module.kogera.jacksonObjectMapper
10+
import org.junit.jupiter.api.Assertions.assertEquals
711
import org.junit.jupiter.api.Assertions.assertTrue
812
import org.junit.jupiter.api.Test
13+
import kotlin.reflect.full.memberProperties
914

1015
class GitHub922 {
1116
private inline fun <reified T : Any> ObjectMapper.introspectSerialization(): BeanDescription =
@@ -29,4 +34,49 @@ class GitHub922 {
2934
assertTrue(desc.isRequired("list"))
3035
assertTrue(desc.isRequired("map"))
3136
}
37+
38+
// isRequired_required_nullability_expected
39+
@Suppress("PropertyName")
40+
data class IsRequiredDto(
41+
// region: isRequired takes precedence
42+
@JsonProperty(isRequired = OptBoolean.FALSE, required = false)
43+
val FALSE_false_nullable_false: String?,
44+
@JsonProperty(isRequired = OptBoolean.FALSE, required = false)
45+
val FALSE_false_nonNull_false: String,
46+
@JsonProperty(isRequired = OptBoolean.FALSE, required = true)
47+
val FALSE_true_nullable_false: String?,
48+
@JsonProperty(isRequired = OptBoolean.FALSE, required = true)
49+
val FALSE_true_nonNull_false: String,
50+
@JsonProperty(isRequired = OptBoolean.TRUE, required = false)
51+
val TRUE_false_nullable_true: String?,
52+
@JsonProperty(isRequired = OptBoolean.TRUE, required = false)
53+
val TRUE_false_nonNull_true: String,
54+
@JsonProperty(isRequired = OptBoolean.TRUE, required = true)
55+
val TRUE_true_nullable_true: String?,
56+
@JsonProperty(isRequired = OptBoolean.TRUE, required = true)
57+
val TRUE_true_nonNull_true: String,
58+
// endregion
59+
// region: If isRequired is the default, only overrides by required = true will work.
60+
@JsonProperty(isRequired = OptBoolean.DEFAULT, required = false)
61+
val DEFAULT_false_nullable_false: String?,
62+
@JsonProperty(isRequired = OptBoolean.DEFAULT, required = false)
63+
val DEFAULT_false_nonNull_true: String,
64+
@JsonProperty(isRequired = OptBoolean.DEFAULT, required = true)
65+
val DEFAULT_true_nullable_true: String?,
66+
@JsonProperty(isRequired = OptBoolean.DEFAULT, required = true)
67+
val DEFAULT_true_nonNull_true: String,
68+
// endregion
69+
)
70+
71+
@Test
72+
fun `JsonProperty properly overrides required`() {
73+
val desc = defaultMapper.introspectDeserialization<IsRequiredDto>()
74+
75+
IsRequiredDto::class.memberProperties.forEach { prop ->
76+
val name = prop.name
77+
val expected = name.split("_").last().toBoolean()
78+
79+
assertEquals(expected, desc.isRequired(name), name)
80+
}
81+
}
3282
}

0 commit comments

Comments
 (0)