FasterXML / FasterXML/jackson-databind

JsonSerialize.keyUsing propagates in values of the Map

Abierto
#2,050 1 comentario 0 reacciones 0 asignados Ver en GitHub
3.x
Lenguaje dominante
Java
Estrellas
3.7k
Forks
1.5k
Merge medio
3 d 6 h
PR fusionados (30 d)
28

Descripción

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_"

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.