Skip to content

When serializing a Map via Converter(StdDelegatingSerializer), a NullPointerException is thrown due to missing key serializer #4878

@k163377

Description

@k163377

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

For a class that wraps a Map, registering and serializing a StdDelegatingSerializer with a Converter that unwraps it will throw the following error

com.fasterxml.jackson.databind.JsonMappingException: Cannot invoke "com.fasterxml.jackson.databind.JsonSerializer.serialize(Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)" because "keySerializer" is null (through reference chain: java.util.ImmutableCollections$Map1["a"])

	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:400)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:359)
	at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:324)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:810)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:763)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:719)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:34)
	at com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer.serialize(StdDelegatingSerializer.java:166)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:503)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:342)
	at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4838)
	at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:4079)
	at com.fasterxml.jackson.databind.Kotlin873Test.directTest(Kotlin873Test.java:61)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: java.lang.NullPointerException: Cannot invoke "com.fasterxml.jackson.databind.JsonSerializer.serialize(Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)" because "keySerializer" is null
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:796)
	... 34 more

Version Information

Confirmed on 2.18(7144db0) and 2.19(6729641) branches.

Reproduction

You can run it by pasting it into src/test/java/com/fasterxml/jackson/databind.

package com.fasterxml.jackson.databind;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer;
import com.fasterxml.jackson.databind.util.StdConverter;
import org.junit.Test;

import java.util.Map;

public class Kotlin873Test {
    // region: DTO Definitions
    static class MapWrapper {
        private final Map<String, Object> value;

        MapWrapper(Map<String, Object> value) {
            this.value = value;
        }

        public Map<String, Object> getValue() {
            return value;
        }
    }
    // endregion

    // region: Jackson settings
    static class WrapperConverter extends StdConverter<MapWrapper, Object> {
        @Override
        public Object convert(MapWrapper value) {
            return value.getValue();
        }
    }

    static class MySerializers extends SimpleSerializers {
        @Override
        public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {
            Class<?> rawClass = type.getRawClass();
            if (MapWrapper.class.isAssignableFrom(rawClass)) {
                return new StdDelegatingSerializer(new WrapperConverter());
            }
            return super.findSerializer(config, type, beanDesc);
        }
    }

    private final ObjectMapper mapper;

    public Kotlin873Test() {
        SimpleModule sm = new SimpleModule();
        sm.setSerializers(new MySerializers());
        mapper = new ObjectMapper().registerModule(sm);
    }
    // endregion

    private final MapWrapper wrapper = new MapWrapper(Map.of("a", 1));

    @Test
    public void directTest() throws JsonProcessingException {
        String json = mapper.writeValueAsString(wrapper);
        System.out.println(json);
    }
}

Expected behavior

At least it shouldn't be a NullPointerException.

Additional context

This error can be suppressed by changing the definition of WrapperConverter as follows to make the specification to generics concrete.

static class WrapperConverter extends StdConverter<MapWrapper, Map<String, Object>> {
    @Override
    public Map<String, Object> convert(MapWrapper value) {
        return value.getValue();
    }
}

This was not done because I was trying to reproduce a problem that had been reported to kotlin-module.

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.18Issues planned at 2.18 or later

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions