杰克逊& JSONAnySetter:序列化/反序列化期间的NullPointer异常

时间:2017-01-27 09:20:26

标签: java json serialization jackson deserialization

我在使用Jackson 1.9.13(以及Jackson 2.5.0)进行序列化/反序列化方面存在问题,并且现在已经打了几天而没有任何成功。

我的目标是使用@JsonAnyGetter& @JsonAnySetter,我想动态计算是否应该将对象写入输出。我有一个JSON定义,我用ObjectMapper序列化(并检查是否应该包含Object),然后我将对象转换回字符串。

我正在使用" HidableSerializer"为此,在序列化期间工作正常,但在将对象转换回字符串时则不行。

如果没有@JsonAnySetter / -getter " HidableSerializer",Everythings工作正常,但不能同时工作。

为什么这不起作用?我该如何解决这个问题?欢迎更好的方法!

堆栈跟踪如下所示:

堆栈跟踪

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

演示代码

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 );

    }

}

测试类

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;
    }

}

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();
}

2 个答案:

答案 0 :(得分:6)

问题是你在 HidableSerializer 中使用的 defaultSerializer 实例是ResolvableSerializer(BeanSerializer),但当你将它包装成<你的 modifySerializer()方法中的em> JsonSerializer (HidableSerializer),它的 resolve()方法永远不会被调用,并且无法正确初始化。

如果您尝试将以下行添加到 HidableSerializer.serialize()方法中:

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

它应该可以解决问题。

如果这适合您,更永久的解决方案是让您的 HidableSerializer 实现 ResolvableSerializer 本身,并将 resolve()委托给 defaultSerializer ,如下所示:

@Override
public void resolve(SerializerProvider serializerProvider) throws JsonMappingException {
    if(defaultSerializer instanceof ResolvableSerializer) {
         ((ResolvableSerializer)defaultSerializer).resolve(serializerProvider);
    }
}

答案 1 :(得分:4)

我经历了调试过程,发现了一些code

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

它实际上实例化了在你的情况下抛出NPE的密钥序列化器。

修改你的HidableSerializer,它会起到作用:

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);
    }
}