Skip to content

@JsonSerialize(Typing.DYNAMIC) in some cases does not override MapperFeature.USE_STATIC_TYPING #1515

@garkin

Description

@garkin

As unit test output shows, annotation overrides mapper feature only for:

  • single field (annotatedValue)

And it's ignored for:

  • annotated collection (annotatedList)
  • annotated class field (dynamicValue )
  • annotated class collection (dynamicList)

This is not expected behavior (for annotated list field, at least), since MapperFeature#USE_STATIC_TYPING says that:

This global default value can be overridden at class, method or field level by using JsonSerialize.typing() annotation property.

Test bellow is written in Kotlin, sorry for that. Was tested with Jackson 2.8.6

package com.testing.jackson

import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import org.junit.Test
import java.util.*
import kotlin.test.assertEquals


abstract class Base { val a = 1 }
class Derived : Base() { val b = 2 }

@JsonSerialize(typing = JsonSerialize.Typing.DYNAMIC)
abstract class BaseDynamic { val a = 1 }
class DerivedDynamic : BaseDynamic() { val b = 2 }

class Mutter {
    val value: Base = Derived()
    @JsonSerialize(typing = JsonSerialize.Typing.DYNAMIC)
    val annotatedValue: Base = Derived()
    val dynamicValue: BaseDynamic = DerivedDynamic()

    val list = ArrayList<Base>().apply { this.add(Derived()) }
    @JsonSerialize(typing = JsonSerialize.Typing.DYNAMIC)
    val annotatedList = ArrayList<Base>().apply { this.add(Derived()) }
    val dynamicList = ArrayList<BaseDynamic>().apply { this.add(DerivedDynamic()) }
}

class jackson_typing_fails {
    @Test fun go() {
        val mapper = ObjectMapper()
                .enable(MapperFeature.USE_STATIC_TYPING)
        //        .enable(SerializationFeature.INDENT_OUTPUT)
        val writer = mapper.writerFor(Mutter::class.java)
        val v = Mutter()
        val s = writer.writeValueAsString(v)
        // println(s)
        val EXPECTED = """{"value":{"a":1},"annotatedValue":{"a":1,"b":2},"dynamicValue":{"a":1,"b":2},"list":[{"a":1}],"annotatedList":[{"a":1,"b":2}],"dynamicList":[{"a":1,"b":2}]}"""
        assertEquals(EXPECTED, s)
    }
}
org.junit.ComparisonFailure: 
Expected :{"value":{"a":1},"annotatedValue":{"a":1,"b":2},"dynamicValue":{"a":1,"b":2},"list":[{"a":1}],"annotatedList":[{"a":1,"b":2}],"dynamicList":[{"a":1,"b":2}]}
Actual   :{"value":{"a":1},"annotatedValue":{"a":1,"b":2},"dynamicValue":{"a":1},"list":[{"a":1}],"annotatedList":[{"a":1}],"dynamicList":[{"a":1}]}

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.2gen-aiPR created with Generative AI (whole or assisted) (or issue for which gen-ai seems suitable)has-failing-testIndicates that there exists a test case (under `failing/`) to reproduce the issue

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions