Reputation: 1268
Your question I have checked issues and docs and could not find solution.
The follwing code properly serialize object (Component
) to string, but deserialization from string back to insgtances of Component
doesn't work.
Any suggestions how to do it, without removing delegations?
Thanks :)
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.core.json.JsonReadFeature
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes(
value = [
Type(value = Space::class, name = Space.TYPE),
Type(value = Command::class, name = Command.TYPE),
]
)
interface Component {
var id: String
@get:JsonIgnore
val type: String
}
data class Command(
val value: String,
val component: Component
) : Component by component {
override val type: String
get() = TYPE
companion object {
const val TYPE = "command"
}
}
data class Space(
val space: String,
private val component: Component
) : Component by component {
override val type: String
get() = TYPE
companion object {
const val TYPE = "space"
}
}
data class Info(
override var id: String,
override val type: String = "",
) : Component
class FooTest {
// private val objectMapper = ObjectMapperFactory.createObjectMapper(emptyList())
private val objectMapper = JsonMapper.builder()
.addModule(KotlinModule())
.serializationInclusion(JsonInclude.Include.NON_NULL)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
.enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)
.build()
@Test
fun `should deserialize`() {
val value = objectMapper.writeValueAsString(
Command(
value = "text",
component = Info(id = "d")
)
)
val obj = objectMapper.readValue(value, Component::class.java)
assertThat(value).isEqualTo(obj)
}
}
Error
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'Info' as a subtype of `<>.<>.<>.<>.jackson.Component`: known type ids = [command, space] (for POJO property 'component')
at [Source: (String)"{"type":"command","value":"text","component":{"type":"Info","id":"d"},"id":"d"}"; line: 1, column: 54] (through reference chain: <>.<>.<>.<>.jackson.Command["component"])
Upvotes: 2
Views: 852
Reputation: 803
Slightly changed your code in order to fix deserialization, as it was failing on creation Info component because of it's not in known JsonSubTypes
list.
data class Info(
override var id: String,
override val type: String = "info",
) : Component
JsonSubTypes
annotation:@JsonSubTypes(
value = [
Type(value = Space::class, name = Space.TYPE),
Type(value = Command::class, name = Command.TYPE),
Type(value = Info::class, name = "info"),
]
)
interface Component
Also small fix in test code:
val before = Command(
value = "text",
component = Info(id = "d")
)
val asString = objectMapper.writeValueAsString(before)
val after = objectMapper.readValue(asString, Component::class.java)
assertThat(after).isEqualTo(before)
Upvotes: 0