Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map key serialization doesn't obey @JsonProperty or @JsonSerialize #2454

Closed
fredgalvao opened this issue Sep 13, 2019 · 4 comments
Closed

Map key serialization doesn't obey @JsonProperty or @JsonSerialize #2454

fredgalvao opened this issue Sep 13, 2019 · 4 comments

Comments

@fredgalvao
Copy link

I was able to reproduce something similar to both #1535 and FasterXML/jackson-module-kotlin#247 with pure java code, on the latest versions of the jackson ecosystem (2.10.0.pr2 at the time).

Problem

I can't customize serialization of Map keys with @JsonProperty(keyUsing, using) nor with @JsonSerialize in a way that is simple (uses a single serializer) and consistent. The issue is seen both on inline Maps and on data class Map properties mapped with @JsonProperty. Serialization of Map values work perfectly in most cases though.

Attached is a sample gradle project with 4 scenarios:

There are 6 tests for each scenario:

  • <1> serialize a simple object A
  • <2> deserialize a simple object A
  • <3> serialize a nested object B with a property Map<A, A>
  • <4> deserialize a nested object B with a property Map<A, A>
  • <5> serialize an inline Map<A, A>
  • <6> deserialize an inline Map<A, A>

All deserialization tests pass, for all scenarios.
The serialization for nested object and inline maps fail for scenarios 1 and 2.
The failures are slightly different in some cases, and exactly the same in other cases.
The behaviour is exactly the same in Java and in Kotlin (just for the sake of argument).

Reproduction

I created a sample project with all source classes and unit tests needed to reproduce the scenarios+cases mentioned. The kotlin version is not attached there, but is available on my comment at #1535 (comment). The project I created here, however, is a lot more robust, reviewed, complete, and clean than the initial kotlin version I had.

https://github.com/fredgalvao/jackson-key-serialization-tests

Extra

Changing the serializer to JsonSerializer as https://www.baeldung.com/jackson-map uses, also changes nothing (as expected, I guess), which actually makes me believe that Baeldung is wrong on that example/tutorial.

If I want to reuse the same serializer on all scenarios:
Based on #661, if I use writeString some fail, if I use writeFieldName some other fail.

If I accept separate serializers
If I make two separate serializers (the reproduction code has this by default), one for A (using writeString) and one for B (using writeFieldName), then 1_3 and 2_3 pass, but the inline map tests still fail, so it's not a solution.

Conclusion

So far, only @JsonValue and @JsonCreator works everywhere, but that shouldn't be the case afaik. Please feel free to point me to a piece of documentation or explanation that would make all of this a "you were using it wrong altogether" or "this is by design". I'll accept the workaround as a final solution with ease (I already did, actually).

@cowtowncoder
Copy link
Member

Ok thank you for filing this here: makes sense as it is NOT kotlin-only (and probably not specific).

@cowtowncoder
Copy link
Member

Ok. I am not quite sure what things you do expect to work: for example, @JsonProperty is not relevant to serialization of Map keys. I guess based on one of linked-to issues, there might be expectation that @JsonProperty of Enum value might be used, although I am not sure exactly how that would work.

But here are the things that should work:

  1. Using @JsonDeserialize(keyUsing = MyKeyDeserializer.class) for property.
  2. Using @JsonDeserialize(keyUsing = MyKeyDeserializer.class) for Map class (custom map subtype) -- NOTE: does NOT use if trying to annotate Class to use as key (although having some way to indicate key deserializer for class would make sense, I think)
  3. Registering key deserializer for a type using Module (SimpleModule.addKeyDeserializer())
  4. @JsonValue / @JsonCreator for key type

Given that, am I right in thinking that what you would like to see is simply an annotation on Class used as key that would point to key deserializer and/or key serializer? Like making this work:

@JsonSerialize(keyUsing = ...)
@JsonDeserialize(keyUsing = ...)
public class KeyType { .... }

or possibly new annotation types (@JsonKeySerializer(serializerClass), @JsonKeyDeserializer(deserializerClass)).

@cowtowncoder
Copy link
Member

Created #2503 to add support for supporting above mentioned:

@JsonSerialize(keyUsing = ...)
@JsonDeserialize(keyUsing = ...)
public class KeyType { .... }

usage.

cowtowncoder added a commit that referenced this issue Oct 16, 2019
@cowtowncoder
Copy link
Member

Closing this assuming #2503 does cover the problem: may be reopened (or new issue filed) for work outside scope of that issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants