最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - How can I flatten a polymorphic field in Kotlin with Jackson so that custom type keys appear at the top level? - Stack Ov

programmeradmin1浏览0评论

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:

  1. 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.
  2. It also uses YAML type tags (!<git> and !<filesystem>) rather than simply a key named "git" or "filesystem".
  3. 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!

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论