Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.projectmapk.jackson.module.kogera.annotationIntrospector

import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.OptBoolean
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.introspect.Annotated
import com.fasterxml.jackson.databind.introspect.AnnotatedField
Expand All @@ -26,13 +28,22 @@ internal class KotlinPrimaryAnnotationIntrospector(
private val nullToEmptyMap: Boolean,
private val cache: ReflectionCache
) : NopAnnotationIntrospector() {
// If a new isRequired is explicitly specified or the old required is true, those values take precedence.
// In other cases, override is done by KotlinModule.
private fun JsonProperty.forceRequiredByAnnotation(): Boolean? = when {
isRequired != OptBoolean.DEFAULT -> isRequired.asBoolean()
required -> true
else -> null
}

// If JsonProperty.required is true, the behavior is clearly specified and the result is paramount.
// Otherwise, the required is determined from the configuration and the definition on Kotlin.
override fun hasRequiredMarker(m: AnnotatedMember): Boolean? {
return cache.getJmClass(m.member.declaringClass)?.let { jmClass ->
// To avoid overwriting processing by other modules, annotations are checked after JmClass has been obtained
_findAnnotation(m, JSON_PROPERTY_CLASS)
?.let { if (it.required) return true }
?.forceRequiredByAnnotation()
?.let { return it }

when (m) {
is AnnotatedField -> m.hasRequiredMarker(jmClass)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package io.github.projectmapk.jackson.module.kogera.zPorted.test.github

import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.OptBoolean
import com.fasterxml.jackson.databind.BeanDescription
import com.fasterxml.jackson.databind.ObjectMapper
import io.github.projectmapk.jackson.module.kogera.KotlinFeature
import io.github.projectmapk.jackson.module.kogera.defaultMapper
import io.github.projectmapk.jackson.module.kogera.jacksonObjectMapper
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import kotlin.reflect.full.memberProperties

class GitHub922 {
private inline fun <reified T : Any> ObjectMapper.introspectSerialization(): BeanDescription =
Expand All @@ -29,4 +34,49 @@ class GitHub922 {
assertTrue(desc.isRequired("list"))
assertTrue(desc.isRequired("map"))
}

// isRequired_required_nullability_expected
@Suppress("PropertyName")
data class IsRequiredDto(
// region: isRequired takes precedence
@JsonProperty(isRequired = OptBoolean.FALSE, required = false)
val FALSE_false_nullable_false: String?,
@JsonProperty(isRequired = OptBoolean.FALSE, required = false)
val FALSE_false_nonNull_false: String,
@JsonProperty(isRequired = OptBoolean.FALSE, required = true)
val FALSE_true_nullable_false: String?,
@JsonProperty(isRequired = OptBoolean.FALSE, required = true)
val FALSE_true_nonNull_false: String,
@JsonProperty(isRequired = OptBoolean.TRUE, required = false)
val TRUE_false_nullable_true: String?,
@JsonProperty(isRequired = OptBoolean.TRUE, required = false)
val TRUE_false_nonNull_true: String,
@JsonProperty(isRequired = OptBoolean.TRUE, required = true)
val TRUE_true_nullable_true: String?,
@JsonProperty(isRequired = OptBoolean.TRUE, required = true)
val TRUE_true_nonNull_true: String,
// endregion
// region: If isRequired is the default, only overrides by required = true will work.
@JsonProperty(isRequired = OptBoolean.DEFAULT, required = false)
val DEFAULT_false_nullable_false: String?,
@JsonProperty(isRequired = OptBoolean.DEFAULT, required = false)
val DEFAULT_false_nonNull_true: String,
@JsonProperty(isRequired = OptBoolean.DEFAULT, required = true)
val DEFAULT_true_nullable_true: String?,
@JsonProperty(isRequired = OptBoolean.DEFAULT, required = true)
val DEFAULT_true_nonNull_true: String,
// endregion
)

@Test
fun `JsonProperty properly overrides required`() {
val desc = defaultMapper.introspectDeserialization<IsRequiredDto>()

IsRequiredDto::class.memberProperties.forEach { prop ->
val name = prop.name
val expected = name.split("_").last().toBoolean()

assertEquals(expected, desc.isRequired(name), name)
}
}
}
Loading