qwetty
qwetty

Reputation: 1268

Deserializing object to data class that contains delegated properties (inheritance with delegation)

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

Answers (1)

Nurlan
Nurlan

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.

  1. Override type field of Info class
data class Info(
    override var id: String,
    override val type: String = "info",
) : Component
  1. Add it to 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

Related Questions