Search code examples
javaexceptionjacksondeserializationstack-trace

Deserialize JSON to Exception object


I'm using Jackson to deserialize an Exception object from JSON. Here's a simplified demo (using an implicit delegating creator):

throw new ObjectMapper().readValue("\"custom error message\"", Exception.class);

This works, but unfortunately the stack trace includes the Jackson frames leading up to the instantiation of the exception:

Exception in thread "main" java.lang.Exception: custom error message
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call1(AnnotatedConstructor.java:133)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:332)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromString(StdDeserializer.java:265)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1495)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:197)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)

Is there a way to clean it up so it looks like it was created on the same line it was thrown?


Solution

  • You can use fillInStackTrace() to reset the stack trace to the point at which you throw:

    public class Main {
        public static void main(String[] args) throws Exception {
            Exception e = createException();
            e.fillInStackTrace();
            throw e;
        }
    
        private static Exception createException() {
            return new Exception("my message");
        }
    }
    

    produces:

    Exception in thread "main" java.lang.Exception: my message
        at org.example.Main.main(Main.java:11)
    

    without the call to `fillInStackTrace() the output is:

    Exception in thread "main" java.lang.Exception: my message
        at org.example.Main.createException(Main.java:16)
        at org.example.Main.main(Main.java:10)