Как мне определить тип для полиморфной десериализации Джексона из родительского объекта

#kotlin #jackson

#kotlin #джексон

Вопрос:

У меня есть модель, которую я не контролирую, которая, например, вкладывает подтип в родительский объект:

 {
  timestamp: 0,
  type: {
     eventType: "Foo"
     majorVersion: 1
     minorVersion: 0
  },
  data: {
    foo: "baz"
  }
}
  

Существующий EXTERNAL_PROPERTY преобразователь типов может обрабатывать только строковое поле, а не объект.

Ответ №1:

Самоответчик, потому что мне было так сложно разобраться. Используйте пользовательский AsPropertyTypeDeserializer . Kotlin упрощает это, автоматически регистрируя закрытые подтипы классов, не требуя аннотаций, с обработкой неизвестного типа в качестве дополнительного бонуса:

 class Event(
    override val timestamp: Long,
    val type: EventType,
    @JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type",
        visible = true
    )
    @JsonTypeResolver(EventDataTypeResolverBuilder::class)
    val data: EventData
)

class EventType(
    val majorVersion: Int,
    val minorVersion: Int,
    val eventType: String
)

sealed class EventData {
    data class Foo(
        val foo: String
    ) : EventData()

    data class Bar(
        val bar: String
    ) : EventData()

    object Unknown: EventData()
}

class EventDataTypeResolverBuilder : StdTypeResolverBuilder() {
    override fun buildTypeDeserializer(
        config: DeserializationConfig,
        baseType: JavaType,
        subtypes: MutableCollection<NamedType>,
    ): TypeDeserializer {
        val idRes = idResolver(
            config,
            baseType,
            subTypeValidator(config),
            subtypes,
            false,
            true
        )
        return EventDataTypeDeserializer(
            baseType,
            idRes,
            _typeProperty,
            _typeIdVisible,
            null
        )
    }
}

class EventDataTypeDeserializer : AsPropertyTypeDeserializer {
    constructor(
        bt: JavaType,
        idRes: TypeIdResolver,
        typePropertyName: String,
        typeIdVisible: Boolean,
        defaultImpl: JavaType?,
    ) : super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl)

    constructor(src: EventDataTypeDeserializer, property: BeanProperty) : super(src, property)

    override fun forProperty(prop: BeanProperty): TypeDeserializer {
        return if (prop === _property) this else EventDataTypeDeserializer(this, prop)
    }

    @Throws(IOException::class)
    override fun deserializeTypedFromObject(p: JsonParser, ctxt: DeserializationContext): Any? {
        val type = p.parsingContext.parent.currentValue as EventType
        val typeId = "EventData$${type.eventType}"
        return _findDeserializer(ctxt, typeId).deserialize(p, ctxt)
    }

    override fun _handleUnknownTypeId(ctxt: DeserializationContext, typeId: String): JavaType {
        return _idResolver.typeFromId(ctxt, "EventData$Unknown")
    }
}