I am facing issues while deserializing Exception
and Throwable
instances using Jackson (version 2.2.1). Consider the following snippet:
public static void main(String[] args) throws IOException
{
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
objectMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
try {
Integer.parseInt("String");
}
catch (NumberFormatException e) {
RuntimeException runtimeException = new RuntimeException(e);
String serializedException = objectMapper.writeValueAsString(runtimeException);
System.out.println(serializedException);
Throwable throwable = objectMapper.readValue(serializedException, Throwable.class);
throwable.printStackTrace();
}
}
The output of System.out.println
in the catch
block is:
{
"@class" : "java.lang.RuntimeException",
"detailMessage" : "java.lang.NumberFormatException: For input string: \"String\"",
"cause" : {
"@class" : "java.lang.NumberFormatException",
"detailMessage" : "For input string: \"String\"",
"cause" : null,
"stackTrace" : [ {
"declaringClass" : "java.lang.NumberFormatException",
"methodName" : "forInputString",
"fileName" : "NumberFormatException.java",
"lineNumber" : 65
}, {
"declaringClass" : "java.lang.Integer",
"methodName" : "parseInt",
"fileName" : "Integer.java",
"lineNumber" : 492
}, {
"declaringClass" : "java.lang.Integer",
"methodName" : "parseInt",
"fileName" : "Integer.java",
"lineNumber" : 527
}, {
"declaringClass" : "test.jackson.JacksonTest",
"methodName" : "main",
"fileName" : "JacksonTest.java",
"lineNumber" : 26
} ],
"suppressedExceptions" : [ "java.util.ArrayList", [ ] ]
},
"stackTrace" : [ {
"declaringClass" : "test.jackson.JacksonTest",
"methodName" : "main",
"fileName" : "JacksonTest.java",
"lineNumber" : 29
} ],
"suppressedExceptions" : [ "java.util.ArrayList", [ ] ]
}
which seems fine. But when I attempt to deserialize this using objectMapper.readValue()
, I get the following exception:
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "declaringClass" (class java.lang.StackTraceElement), not marked as ignorable
at [Source: java.io.StringReader@3c5ebd39; line: 9, column: 27] (through reference chain: java.lang.StackTraceElement["declaringClass"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:555)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:708)
at com.fasterxml.jackson.databind.deser.std.JdkDeserializers$StackTraceElementDeserializer.deserialize(JdkDeserializers.java:414)
at com.fasterxml.jackson.databind.deser.std.JdkDeserializers$StackTraceElementDeserializer.deserialize(JdkDeserializers.java:380)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:151)
...
I then tried using mix-in annotations, to ignore declaringClass
in java.lang.StackTraceElement
, but now the deserialized Exception
doesn't contain the declaring class in its stack trace:
java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "String"
at .main(JacksonTest.java:33)
Caused by: java.lang.NumberFormatException: For input string: "String"
at .forInputString(NumberFormatException.java:65)
at .parseInt(Integer.java:492)
at .parseInt(Integer.java:527)
at .main(JacksonTest.java:30)
Am I missing anything? Any help is greatly appreciated.
It seems that the output you get in version 2.2.1 is not the same as I get with version 2.2.0 (which according to the website is the latest 2.x version). Besides the latest available 2.x version on the Maven Repository is 2.2.2. So I would try to either downgrade it to 2.2.0 or to upgrade it to 2.2.2. If any of the changes brings you the expected result, I would go further with that version and open a BUG in Jackson's JIRA.
And of course don't forget
objectMapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
from Michael's answer.
Try using polymorphism so that jackson deserializer knows what kind of Throwable to create:
/**
* Jackson module to serialize / deserialize Throwable
*/
public class ThrowableModule extends SimpleModule {
public ThrowableModule() {
super("Throwable", new Version(1, 0, 0, null, null, null));
}
@Override
public void setupModule(SetupContext context) {
context.setMixInAnnotations(Throwable.class, ThrowableAnnotations.class);
}
/**
* Add annotation to Throwable so that the class name is serialized with the instance data.
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class")
static abstract class ThrowableAnnotations {
}
}
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