I am facing issues while deserializing Exception
and Throwable
instances using Jackson (version 2.2.1). Consider the following snippet:
There seems to be a Jackson JIRA entry for this here. Jackson doesn't seem to be able to handle the declaringClass
in java.lang.StackTraceElement
, since the getter corresponding to this field is called getClassName()
.
I fixed this issue by using a custom wrapper around StackTraceElement
as suggested in the JIRA entry mentioned above. The custom wrapper (CustomStackTraceElement
) will have the fields declaringClass
, methodName
, fileName
, and lineNumber
and the corresponding getters and setters in it. I modified the catch
block (mentioned in the question) to be as follows:
catch (NumberFormatException e) {
RuntimeException runtimeException = new RuntimeException(e);
e.printStackTrace();
String serializedException = objectMapper.writeValueAsString(runtimeException);
System.out.println(serializedException);
String serializedStackTrace = objectMapper.writeValueAsString(transformStackTrace(runtimeException));
String serializedStackTraceForCause = objectMapper.writeValueAsString(transformStackTrace(runtimeException.getCause()));
Throwable throwable = objectMapper.readValue(serializedException, Throwable.class);
List customStackTraceElementList = objectMapper.readValue(serializedStackTrace, List.class);
List customStackTraceElementListForCause = objectMapper.readValue(serializedStackTraceForCause, List.class);
throwable.setStackTrace(reverseTransformStackTrace(customStackTraceElementList));
throwable.getCause().setStackTrace(reverseTransformStackTrace(customStackTraceElementListForCause));
throwable.printStackTrace();
}
The StackTraceElement[]
will be converted into List
by the following method during serialization:
private static List transformStackTrace(Throwable throwable)
{
List list = new ArrayList<>();
for (StackTraceElement stackTraceElement : throwable.getStackTrace()) {
CustomStackTraceElement customStackTraceElement =
new CustomStackTraceElement(stackTraceElement.getClassName(),
stackTraceElement.getMethodName(),
stackTraceElement.getFileName(),
stackTraceElement.getLineNumber());
list.add(customStackTraceElement);
}
return list;
}
... and the reverse transformation will be done during deserialization:
private static StackTraceElement[] reverseTransformStackTrace(List customStackTraceElementList)
{
StackTraceElement[] stackTraceElementArray = new StackTraceElement[customStackTraceElementList.size()];
for (int i = 0; i < customStackTraceElementList.size(); i++) {
CustomStackTraceElement customStackTraceElement = customStackTraceElementList.get(i);
StackTraceElement stackTraceElement =
new StackTraceElement(customStackTraceElement.getDeclaringClass(),
customStackTraceElement.getMethodName(),
customStackTraceElement.getFileName(),
customStackTraceElement.getLineNumber());
stackTraceElementArray[i] = stackTraceElement;
}
return stackTraceElementArray;
}
Now, after deserialization, the Throwable
object has the expected stack trace in it.