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();
}
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.
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".
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);
}
}
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With