Skip to content

Commit

Permalink
Merge pull request #240 from shartte/sealed-class-subtypes
Browse files Browse the repository at this point in the history
Discover Subclasses of a sealed class automatically
  • Loading branch information
cowtowncoder authored Aug 1, 2019
2 parents b2183ab + f3c29d1 commit d5d7652
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ package com.fasterxml.jackson.module.kotlin
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.Module
import com.fasterxml.jackson.databind.introspect.AnnotatedField
import com.fasterxml.jackson.databind.introspect.AnnotatedMember
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector
import com.fasterxml.jackson.databind.introspect.*
import com.fasterxml.jackson.databind.jsontype.NamedType
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
Expand Down Expand Up @@ -41,6 +38,26 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon
}
}

/**
* Subclasses can be detected automatically for sealed classes, since all possible subclasses are known
* at compile-time to Kotlin. This makes [com.fasterxml.jackson.annotation.JsonSubTypes] redundant.
*/
override fun findSubtypes(a: Annotated): MutableList<NamedType>? {

val rawType = a.rawType
if (rawType.isKotlinClass()) {
val kClass = rawType.kotlin
if (kClass.isSealed) {
return kClass.sealedSubclasses
.map { NamedType(it.java) }
.toMutableList()
}
}

return null

}

private fun AnnotatedField.hasRequiredMarker(): Boolean? {
val byAnnotation = (member as Field).getAnnotationsByType(JsonProperty::class.java).firstOrNull()?.required
val byNullability = (member as Field).kotlinProperty?.returnType?.isRequired()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.fasterxml.jackson.module.kotlin.test

import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.annotation.JsonTypeName
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import org.junit.Test
import kotlin.test.assertEquals

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
sealed class Github239Either {

@JsonTypeName("a")
data class A(var field: String = "") : Github239Either()

@JsonTypeName("b")
data class B(var otherField: String = "") : Github239Either()

}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes(
JsonSubTypes.Type(Github239EitherCustomized.A::class, name = "a"),
JsonSubTypes.Type(Github239EitherCustomized.B::class, name = "b")
)
sealed class Github239EitherCustomized {

data class A(var field: String = "") : Github239EitherCustomized()

data class B(var otherField: String = "") : Github239EitherCustomized()

}

class TestGithub239 {
val json = """[
{
"@type": "a",
"field": "value"
},
{
"@type": "b",
"otherField": "1234"
}
]"""

val mapper = ObjectMapper()
.registerModule(KotlinModule())

@Test
fun test_implicit_subclasses() {

val array = mapper.readValue<Array<Github239Either>>(json)

assertEquals(2, array.size)
assertEquals(Github239Either.A("value"), array[0])
assertEquals(Github239Either.B("1234"), array[1])

}

@Test
fun test_explicit_subclasses() {

val array = mapper.readValue<Array<Github239EitherCustomized>>(json)

assertEquals(2, array.size)
assertEquals(Github239EitherCustomized.A("value"), array[0])
assertEquals(Github239EitherCustomized.B("1234"), array[1])

}

}

0 comments on commit d5d7652

Please sign in to comment.