I'm using Jackson (with YAML support) in a Kotlin project where I have a polymorphic field that can be one of several types. I want the YAML output to use a custom type key (e.g., "git"
or "filesystem"
) at the top level instead of a property named contractSource
or YAML type tags.
My Kotlin data classes are defined as follows:
data class ContractConfig(
val contractSource: ContractSource? = null,
val provides: List<String>? = null,
val consumes: List<String>? = null
)
@JsonTypeInfo(use = Id.CUSTOM)
@JsonTypeIdResolver(value = ContractSourceIdResolver::class)
interface ContractSource
data class GitContractSource(
val url: String? = null,
val branch: String? = null
) : ContractSource
data class FileSystemContractSource(
val directory: String = "."
) : ContractSource
My custom type id resolver maps types to IDs as follows:
class ContractSourceIdResolver : TypeIdResolverBase() {
override fun idFromValue(value: Any): String {
return when (value) {
is GitContractSource -> "git"
else -> "filesystem"
}
}
override fun idFromValueAndType(value: Any, suggestedType: Class<*>): String = idFromValue(value)
override fun getMechanism(): Id = Id.CUSTOM
override fun typeFromId(context: DatabindContext, id: String?): JavaType {
return when (id) {
"git" -> context.constructType(GitContractSource::class.java)
else -> context.constructType(FileSystemContractSource::class.java)
}
}
}
And this is my ObjectMapper
configuration:
val objectMapper = ObjectMapper(YAMLFactory()).apply {
registerKotlinModule()
setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
}
However, when I serialize a ContractConfig instance, I get output like this:
- contractSource: !<git>
url: "http://gitRepo"
branch: "1.0.1"
provides:
- "a.yaml"
- "b.yaml"
consumes:
- "c.yaml"
- "d.yaml"
- contractSource: !<filesystem>
directory: "repos"
provides:
- "1.yaml"
- "2.yaml"
consumes:
- "3.yaml"
- "4.yaml"
Problem:
- The YAML output still contains a
contractSource
property wrapper instead of directly placing the custom type key (i.e."git"
or"filesystem"
) at the top level. - It also uses YAML type tags (
!<git>
and!<filesystem>
) rather than simply a key named"git"
or"filesystem"
. - I need to be able to deserialize the resulting YAML back into my Kotlin classes.
I would like the output to be flattened so that the type key appears directly, like this:
- git:
url: "http://gitRepo"
branch: "1.0.1"
provides:
- "a.yaml"
- "b.yaml"
consumes:
- "c.yaml"
- "d.yaml"
- filesystem:
directory: "repos"
provides:
- "1.yaml"
- "2.yaml"
consumes:
- "3.yaml"
- "4.yaml"
Questions:
- Is it possible to achieve this flattened structure—without the contractSource wrapper and YAML type tags—using Jackson annotations (including JsonTypeInfo.Id.CUSTOM), and still support deserialization?
- If so, what adjustments should I make to my configuration or annotations to have the custom type key appear at the top level for both serialization and deserialization?
- Are there alternative approaches (preferably without writing a full custom serializer/deserializer) to obtain the desired YAML structure?
Any guidance or suggestions would be greatly appreciated!