Last active
July 25, 2025 12:57
-
-
Save Anton3/348e639b6d46c3598f3311b9feca8578 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package name.anton3.vkapi.generator.json | |
| import com.fasterxml.jackson.annotation.JsonUnwrapped | |
| import com.fasterxml.jackson.core.JsonParser | |
| import com.fasterxml.jackson.databind.* | |
| import com.fasterxml.jackson.databind.deser.ContextualDeserializer | |
| import com.fasterxml.jackson.databind.deser.ResolvableDeserializer | |
| import com.fasterxml.jackson.databind.deser.std.StdDeserializer | |
| import com.fasterxml.jackson.databind.node.ObjectNode | |
| import com.fasterxml.jackson.databind.node.TreeTraversingParser | |
| import com.fasterxml.jackson.databind.util.NameTransformer | |
| class SinglePolyUnwrappedDeserializer<T : Any> : JsonDeserializer<T>(), ContextualDeserializer { | |
| override fun deserialize(p: JsonParser, ctxt: DeserializationContext): T = error("Not implemented") | |
| override fun createContextual(ctxt: DeserializationContext, property: BeanProperty?): JsonDeserializer<T> = | |
| SinglePolyUnwrappedDeserializerImpl(ctxt) | |
| } | |
| private class SinglePolyUnwrappedDeserializerImpl<T : Any>(ctxt: DeserializationContext) : | |
| StdDeserializer<T>(null as JavaType?) { | |
| private val type: JavaType = ctxt.contextualType | |
| private val beanDeserializer: JsonDeserializer<T> | |
| private val ownPropertyNames: Set<String> | |
| private val unwrappedType: JavaType | |
| private val unwrappedPropertyName: String | |
| private val nameTransformer: NameTransformer | |
| init { | |
| val description: BeanDescription = ctxt.config.introspect(type) | |
| var tempUnwrappedAnnotation: JsonUnwrapped? = null | |
| val unwrappedProperties = description.findProperties().filter { prop -> | |
| listOfNotNull(prop.constructorParameter, prop.mutator, prop.field).any { member -> | |
| val unwrappedAnnotation: JsonUnwrapped? = member.getAnnotation(JsonUnwrapped::class.java) | |
| if (unwrappedAnnotation != null) { | |
| tempUnwrappedAnnotation = unwrappedAnnotation | |
| member.allAnnotations.add(notUnwrappedAnnotation) | |
| } | |
| unwrappedAnnotation != null | |
| } | |
| } | |
| val unwrappedProperty = when (unwrappedProperties.size) { | |
| 0 -> error("@JsonUnwrapped properties not found in ${type.typeName}") | |
| 1 -> unwrappedProperties.single() | |
| else -> error("Multiple @JsonUnwrapped properties found in ${type.typeName}") | |
| } | |
| nameTransformer = tempUnwrappedAnnotation!!.run { NameTransformer.simpleTransformer(prefix, suffix) } | |
| unwrappedPropertyName = unwrappedProperty.name | |
| ownPropertyNames = description.findProperties().mapTo(mutableSetOf()) { it.name } | |
| ownPropertyNames.remove(unwrappedPropertyName) | |
| ownPropertyNames.removeAll(description.ignoredPropertyNames) | |
| unwrappedType = unwrappedProperty.primaryType | |
| val rawBeanDeserializer = ctxt.factory.createBeanDeserializer(ctxt, type, description) | |
| (rawBeanDeserializer as? ResolvableDeserializer)?.resolve(ctxt) | |
| @Suppress("UNCHECKED_CAST") | |
| beanDeserializer = rawBeanDeserializer as JsonDeserializer<T> | |
| } | |
| override fun deserialize(p: JsonParser, ctxt: DeserializationContext): T { | |
| val node = p.readValueAsTree<ObjectNode>() | |
| val ownNode = ObjectNode(ctxt.nodeFactory) | |
| val unwrappedNode = ObjectNode(ctxt.nodeFactory) | |
| node.fields().forEach { (key, value) -> | |
| val transformed: String? = nameTransformer.reverse(key) | |
| if (transformed != null && key !in ownPropertyNames) { | |
| unwrappedNode.replace(transformed, value) | |
| } else { | |
| ownNode.replace(key, value) | |
| } | |
| } | |
| ownNode.replace(unwrappedPropertyName, unwrappedNode) | |
| val syntheticParser = TreeTraversingParser(ownNode) | |
| syntheticParser.nextToken() | |
| return beanDeserializer.deserialize(syntheticParser, ctxt) | |
| } | |
| private class NotUnwrapped( | |
| @Suppress("unused") | |
| @field:JsonUnwrapped(enabled = false) | |
| @JvmField | |
| val dummy: Nothing | |
| ) | |
| companion object { | |
| val notUnwrappedAnnotation: JsonUnwrapped = | |
| NotUnwrapped::class.java.getField("dummy").getAnnotation(JsonUnwrapped::class.java) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This deserializer saved me after 2 days of desperation. I can't believe that Jackson still doesn't support polymorphism on Unwrapped properties