FasterXML / jackson-databind

General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s)
Apache License 2.0
3.52k stars 1.38k forks source link

Serializing Map with Enum as Key: inconsistent behavior between root value, property value #2159

Open ElliotZhang opened 6 years ago

ElliotZhang commented 6 years ago

We just upgraded to Jackson 2.9.6 and found an inconsistent behavior in Map serializing: When the map has enum as key (and the enums implement a common interface), the serializing result jsons are different between:

  1. when the map is serialized as root object, the key will be serialized by Enum.name()
  2. when the map is a property of another object, the key will be serialized by toString()

in our case, we override the toString() of Enum to serve some other purpose, and hence in the above two scenarios, we have different map key seriaization, one with Enum.name(), one with toString()

I've attached a simple test case here: test-jackson.zip

Notice the test will fail in 2.9.6, while in 2.7.4 it will pass. I haven't had time to check which version introduced this issue though.

cowtowncoder commented 6 years ago

Quick question: could you cut'n paste configuration of ObjectMapper here (I know it's in zip, but easier to browse here if directly included)?

As a general rule, I strongly encourage avoiding using generic types (Maps, Collections) as root values, due to additional problems there usually are. This is not to say that there should be problems, just that there is a whole class of issues that come from the Type Erasure that plagues root values. So life is easier when root value is non-generic POJO, and static typing is available through hierarchy.

But that's neither here nor there wrt this problem. Just something to consider as a work-around until problem is found.

ElliotZhang commented 6 years ago

@cowtowncoder the object mapper in the show case is using default settings (just by new ObjectMapper()), nothing else. I reckon this is not a configuration problem. I don't think the recommendation will help. If the serializing behavior is inconsistent I really think it should be sth to be fixed. From what I saw, in scenario 2 the Enum key is not recognized as enum, therefore serialized by toString(). Again we had been using Jackson 2.7.4 with Enum.name() for serialization and toString() for sth else, so enabling SerializationFeature.WRITE_ENUMS_USING_TO_STRING is not an option for us. We've already applied other workaround with KeyUsing but this is not universal solution and really specific to the problematic class.

cowtowncoder commented 5 years ago

Unfortunately I do not think it is necessarily possible to make behavior the same: the problem being that due to Java Type Erasure, Map as root value does not retain enough type information, whereas Map as property value will. I think that earlier versions may have worked the way that was better for you, but I do not think that was by design.

I will leave this issue open, however, to see if behavior could be improved.