Десериализация объекта в класс данных, содержащий делегированные свойства (наследование с делегированием)

Ваш вопрос Я проверил проблемы и документы и не нашел решения.

Следующий код правильно сериализует объект () в строку, но десериализацию из строки обратно в элементы Component не работает.

Есть предложения, как это сделать, не удаляя делегации?

Спасибо :)

      
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)
    }
}

Ошибка

      
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"])

1 ответ

Немного изменил ваш код, чтобы исправить десериализацию, так как он не работал при создании компонента Info из-за того, что его нет в известном списке.

  1. Поле типа переопределения класса Info
      data class Info(
    override var id: String,
    override val type: String = "info",
) : Component
  1. Добавить в JsonSubTypes аннотация:
      @JsonSubTypes(
     value = [
         Type(value = Space::class, name = Space.TYPE),
         Type(value = Command::class, name = Command.TYPE),
         Type(value = Info::class, name = "info"),
     ]
 )
 interface Component

Также небольшое исправление в тестовом коде:

              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)
Другие вопросы по тегам