Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson & JSONAnySetter: NullPointer Exception during Serialization/Deserialization

I have a problem with serialization/deserialization with Jackson 1.9.13 (and Jackson 2.5.0) and fighting this now for a few days without any success.

My goal is to use @JsonAnyGetter & @JsonAnySetter, and I want to calculate dynamically if a object should be written to the output or not. I have a JSON definition which I serialize with the ObjectMapper (and check if the Object should be included or not), and then I convert the object back to a string.

I am using a "HidableSerializer" for this, which works fine during serialization, but not when converting the object back to a string.

Without @JsonAnySetter / -getter or the "HidableSerializer", everythings works fine, but not both together.

Why is this not working? And how can I solve the problem? Better approaches are welcome!

The stack trace looks like this:

Stack Trace

org.codehaus.jackson.map.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: ch.hasselba.Test["[anySetter]"])
null
    at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:218)
    at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:183)
    at org.codehaus.jackson.map.ser.std.SerializerBase.wrapAndThrow(SerializerBase.java:140)
    at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:158)
    at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at ch.hasselba.HidableSerializer.serialize(HidableSerializer.java:29)
    at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:610)
    at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
    at org.codehaus.jackson.map.ObjectMapper._configAndWriteValue(ObjectMapper.java:2575)
    at org.codehaus.jackson.map.ObjectMapper.writeValueAsString(ObjectMapper.java:2097)
    at ch.hasselba.Demo.main(Demo.java:54)
Caused by: java.lang.NullPointerException
    at org.codehaus.jackson.map.ser.std.MapSerializer.serializeFields(MapSerializer.java:243)
    at org.codehaus.jackson.map.ser.AnyGetterWriter.getAndSerialize(AnyGetterWriter.java:41)
    at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:154)
    ... 7 more

The Demo code

package ch.hasselba;

import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.introspect.BasicBeanDescription;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.map.ser.BeanSerializerModifier;

public class Demo {

    public static void main(String[] args) {

        ObjectMapper mapper = new ObjectMapper();

        // register the module
        Version version = new Version(1, 0, 0, "SNAPSHOT");
        mapper.registerModule(new SimpleModule("HidableModule", version) {
            @Override
            public void setupModule(SetupContext context) {
                super.setupModule(context);
                context.addBeanSerializerModifier(new BeanSerializerModifier() {
                    @SuppressWarnings("unchecked")
                    @Override
                    public JsonSerializer<?> modifySerializer(SerializationConfig config, BasicBeanDescription desc,
                        JsonSerializer<?> serializer) {
                        if (IHidable.class.isAssignableFrom(desc.getBeanClass())) {
                            return new HidableSerializer<Object>((JsonSerializer<Object>) serializer);
                        }
                        return serializer;
                    }
                });
            }
        });

        // the data
        String content = "{ \"foo\": \"bar\" }";

        // build the Object
        Test test = null;
        try {
            test =  mapper.readValue(content, Test.class);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // and now convert it back to a String
        String data = null;
        try {
             data = mapper.writeValueAsString(test);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println( data );

    }

}

Test class

package ch.hasselba;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.annotate.JsonAnySetter;

public class Test implements IHidable {

    private Map<String, Object> others = new ConcurrentHashMap<String, Object>();

    @JsonAnyGetter
    public Map<String, Object> getOthers() {
        return this.others;
    }

    @JsonAnySetter
    public void addOther(final String name, final Object value) {   
        this.others.put(name, value);
    }

    @Override
    public boolean isHidden() {
        return false;
    }

}

The Hidable Serializer

package ch.hasselba;

import java.io.IOException;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

public class HidableSerializer<T> extends JsonSerializer<T> {

    private JsonSerializer<T> defaultSerializer;

    public HidableSerializer(JsonSerializer<T> serializer) {
        defaultSerializer = serializer;
    }

    @Override
    public void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
            throws IOException, JsonProcessingException {

        if( value instanceof IHidable ){
            IHidable hidableValue = (IHidable) value;
            if( hidableValue.isHidden() )
                return;
        }
        defaultSerializer.serialize(value, jgen, provider);

    }

}

IHidableInterface

package ch.hasselba;


public interface IHidable {
    boolean isHidden();
}
like image 913
Sven Hasselbach Avatar asked Jan 27 '17 09:01

Sven Hasselbach


People also ask

Is Jackson Wang Korean or Chinese?

His Cantonese comes from his Hong Kong roots, and although he is not a Mandarin native speaker, Wang is fluent in the language as well as in Korean and English. Although a major star in the K-pop realm, Wang is outspoken about his Hong Kong roots.

Is Jackson a K-pop Idol?

Jackson Wang (잭슨왕, or simply as Jackson) is a Hong Kong rapper, singer-songwriter, composer and producer under Team Wang. He is a member of the boy group GOT7 and co-ed hip hop group PANTHEPACK. He made his his solo debut on August 26, 2017 with the single "Papillon".


2 Answers

The problem is that the defaultSerializer instance, you are using inside your HidableSerializer, is a ResolvableSerializer (BeanSerializer), but as you wrap it into JsonSerializer (HidableSerializer) in your modifySerializer() method, it's resolve() method is then never invoked and it fails to initialize properly.

If you try adding the following line to your HidableSerializer.serialize() method:

...
((ResolvableSerializer)defaultSerializer).resolve(provider);
defaultSerializer.serialize(value, jgen, provider);
...

it should do the trick.

If this works for you, a more permanent solution would be to make your HidableSerializer implement ResolvableSerializer itself and just delegate resolve() to the defaultSerializer, like this:

@Override
public void resolve(SerializerProvider serializerProvider) throws JsonMappingException {
    if(defaultSerializer instanceof ResolvableSerializer) {
         ((ResolvableSerializer)defaultSerializer).resolve(serializerProvider);
    }
}
like image 134
zeppelin Avatar answered Oct 06 '22 00:10

zeppelin


I went through debugging process and found some code:

if (ser instanceof ResolvableSerializer) {
    ((ResolvableSerializer) ser).resolve(provider);
}

It actually instantiates key serializer which throws NPE in your case.

Modify your HidableSerializer and it will do the trick:

public class HidableSerializer<T> extends JsonSerializer<T> implements ResolvableSerializer {

    private JsonSerializer<T> defaultSerializer;

    public HidableSerializer(JsonSerializer<T> serializer) {
        defaultSerializer = serializer;
    }

    @Override
    public void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonProcessingException {

        if( value instanceof IHidable ){
            IHidable hidableValue = (IHidable) value;
            if( hidableValue.isHidden() )
                return;
        }
        defaultSerializer.serialize(value, jgen, provider);

    }

    @Override
    public void resolve(SerializerProvider provider) throws JsonMappingException {
        ((ResolvableSerializer)defaultSerializer).resolve(provider);
    }
}
like image 44
Max Avatar answered Oct 06 '22 00:10

Max