-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Search before asking
- I searched in the issues and found nothing similar.
Describe the bug
When serializing a field of type Map<K, V>, Jackson will inspect the declared K type for serialization annotations, rather than the individual entries key's instance type. This creates issues where K is a supertype and the map holds subtypes of K (K') that do specify serialization annotations.
Values are serialized as expected and the instance type is used for annotation introspection.
Version Information
2.18.0
Reproduction
public final class Main {
public static void main(final String[] args) throws IOException {
final var list = new IntOrStringList(List.of(new IntOrString.StringValue("foo"), new IntOrString.IntValue(123)));
final var map = new IntOrStringMap(Map.of(new IntOrString.StringValue("bar"), new IntOrString.IntValue(456)));
final var mapper = new JsonMapper();
System.out.println(mapper.writeValueAsString(list));
System.out.println(mapper.writeValueAsString(map));
System.out.println(mapper.writeValueAsString(Map.of(new IntOrString.StringValue("baz"), new IntOrString.IntValue(789))));
}
public record IntOrStringList(List<IntOrString> values) {
}
public record IntOrStringMap(Map<IntOrString, IntOrString> values) {
}
public sealed interface IntOrString {
@JsonKey(false) String forMapKeySerialization();
record IntValue(@JsonValue int value) implements IntOrString {
@Override
public String forMapKeySerialization() {
return String.valueOf(this.value);
}
}
record StringValue(@JsonValue String value) implements IntOrString {
@Override
public String forMapKeySerialization() {
return this.value;
}
}
}
}Expected behavior
In the reproducer above, the first println prints {"values":["foo",123]}, the second println prints {"values":{"StringValue[value=bar]":456}}, using the default toString() for the key. The third println is included to display that the issue is when serializing a map field, not a map value directly as that third prints {"baz":789} which is the kind of serialization behavior I'd expect for the second case as well.
Enabling the @JsonKey in the interface's method makes it behave how I'd expect, and the second println now prints {"values":{"bar":456}}. The annotation's presence when disabled doesn't play a role, as it doesn't behave any differently when it isn't there (as expected).
Additional context
The most relevant issue I could find is #3119 given the inheritance setting, but explicitly specifying @JsonSerialize(keyUsing = ...) behaves correctly so I ruled that out, but they might be related in a way I can't see.