I am trying to create a class with a java.lang.Exception
stored as a field. Also I am trying to exclude stack trace from serialization/deserialization using @JsonIgnoreProperties
annotation.
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
class ExWrapper {
@JsonIgnoreProperties({"stackTrace"})
public Exception ex;
@Override
public String toString() {
return "ExWrapper{" +
"ex=" + ex +
'}';
}
}
public class Example {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ExWrapper exw = new ExWrapper();
exw.ex = new Exception("Oops");
String str = mapper.writeValueAsString(exw);
System.out.println(str);
ExWrapper exW = mapper.readValue(str, ExWrapper.class);
System.out.println(exW);
}
}
The result error is quite surprising, Jackson cannot find the message
field:
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "message" (class java.lang.Exception), not marked as ignorable (one known property: "cause"])
at [Source: (String)"{"ex":{"cause":null,"message":"Oops","suppressed":[],"localizedMessage":"Oops"}}"; line: 1, column: 32] (through reference chain: ExWrapper["ex"]->java.lang.Exception["message"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:840)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1179)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1592)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1570)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:375)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3205)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3173)
at Example.main(Example.java:25)
Well, there is only getMessage
and no setMessage
in Throwable
class so it seems a bit reasonable unless you try to remove @JsonIgnoreProperties
annotation. It works like a charm: it serializes and deserializes back properly and missing setter for message
is suddenly not a problem. Adding "message"
to ignored fields also make it work (yet without an exception message).
I tried to randomly step in Jackson code using debugger and found out that when @JsonIgnoreProperties
is missing, eventually ThrowableDeserializer
methods are called and they are not called when the annotation is present. ThrowableDeserializer
seems to have some hacks specific to exception message. My guess is that ThrowableDeserializer
is not viable when the stack trace is missing and Jackson falls back to default java bean serializer.
The question is what exactly is going on here and how to solve it.