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

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

Open
1 task done
k163377 opened this issue Dec 31, 2024 · 1 comment
Labels
to-evaluate Issue that has been received but not yet evaluated

Comments

@k163377
Copy link
Contributor

k163377 commented Dec 31, 2024

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.

@k163377 k163377 added the to-evaluate Issue that has been received but not yet evaluated label Dec 31, 2024
@cowtowncoder
Copy link
Member

Agreed, at very least should not throw NPE. Hoping to look into this soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
to-evaluate Issue that has been received but not yet evaluated
Projects
None yet
Development

No branches or pull requests

2 participants