FasterXML / FasterXML/jackson-databind

JsonSerialize.keyUsing propagates in values of the Map

Aperta
#2,050 1 commento 0 reazioni 0 assegnatari Vedi su GitHub
3.x
Lingua principale
Java
Stelle
3.7k
Fork
1.5k
Merge medio
3g 6h
PR unite (30g)
28

Descrizione

Hi,

I spotted a strange behaviour using `JsonSerialize.keyUsing`. I spotted it using the last version `2.9.5`.

Here is the test class:

```java
@DisplayName("Demo of jackson JsonSerialize annotation with keyUsing attribute")
public class JacksonKeyUsing {

private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

private static class ObjectKeySerializer extends JsonSerializer {

public void serialize(ObjectKey objectKey,
JsonGenerator generator,
SerializerProvider provider) throws IOException {
generator.writeFieldName(objectKey.getId()+": "+objectKey.getName());
}
}

@Data
private static class ObjectKey {

private final Integer id;

private final String name;

@Builder
ObjectKey(@NonNull Integer id,
@NonNull String name){
this.id = id;
this.name = name;
}
}

@Data
@Builder
private static class ObjectKeyValues {

@JsonProperty("attributes")
@JsonSerialize(keyUsing = ObjectKeySerializer.class)
@Singular
private Map attributes;

}

@Test
@DisplayName("Serialize a complex key<->simple value Map")
public void object_key_simple_value_serialize() throws JsonProcessingException {
// GIVEN
final ObjectKeyValues data = ObjectKeyValues.builder()
.attribute(ObjectKey.builder()
.id(1)
.name("First")
.build(),
1)
.attribute(ObjectKey.builder()
.id(2)
.name("Second")
.build(),
"2")
.build();

// WHEN
String result = OBJECT_MAPPER.writeValueAsString(data);

// THEN
assertThat(result).isEqualTo("{\"attributes\":{\"1: First\":1,\"2: Second\":\"2\"}}");
}

@Test
@DisplayName("Serialize a complex key<->complex value Map")
public void object_key_complex_value_serialize() throws JsonProcessingException {
// GIVEN
final ObjectKeyValues data = ObjectKeyValues.builder()
.attribute(ObjectKey.builder()
.id(1)
.name("First")
.build(),
singletonMap("key", "value"))
.build();

// WHEN
String result = OBJECT_MAPPER.writeValueAsString(data);

// THEN
assertThat(result).isEqualTo("{\"attributes\":{\"1: First\":{\"key\":\"value\"}}}");
}

}
```

In this class, two tests:

1. `object_key_simple_value_serialize` put mere value-objects in the map serialized with a custom serializer for keys. This test is OK.
2. `object_key_complex_value_serialize` put a `Map` as a value. This test fails with the following error:

```
com.fasterxml.jackson.databind.JsonMappingException: java.lang.String cannot be cast to JacksonKeyUsing$ObjectKey (through reference chain: JacksonKeyUsing$ObjectKeyValues["attributes"]->java.util.Collections$SingletonMap["JacksonKeyUsing.ObjectKey(id=1, name=First)"]->java.util.Collections$SingletonMap["key"])

at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:391)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:721)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:639)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:718)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:639)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3893)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3207)
at JacksonKeyUsing.object_key_complex_value_serialize(JacksonKeyUsing.java:100)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:389)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:167)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:163)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:110)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute$3(HierarchicalTestExecutor.java:83)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:77)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$null$2(HierarchicalTestExecutor.java:92)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute$3(HierarchicalTestExecutor.java:92)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:77)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$null$2(HierarchicalTestExecutor.java:92)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute$3(HierarchicalTestExecutor.java:92)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:77)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:51)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to JacksonKeyUsing$ObjectKey
at JacksonKeyUsing$ObjectKeySerializer.serialize(JacksonKeyUsing.java:26)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:707)
... 64 more
```

My guess is that the `JsonSerialize.keyUsing` sets a context in the `Map` serialization and that this context propagates to values too. Hence, if a `Map` is put as a value of the first `Map`, it attempts to use the serializer specified to serialize the keys inside the "_value Map_"

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.