Search code examples
javagson

GSON unable to serialize exception with Java 17


The following code used to work on Java 11:

new Gson().toJson(new Exception())

On JDK 17 I get the following error:

Unable to make field private java.lang.String java.lang.Throwable.detailMessage accessible: module java.base does not "opens java.lang" to unnamed module @147ed70f

From reading this page, I think I could resolve it with --add-opens java.base/java.lang=ALL-UNNAMED. Is there a better way however? Perhaps with a custom de/serializer?


Solution

  • Here's the code I added to de/serialize exceptions. This can be used in a class like this:

    public class Result {
        public final Object result;
        public final Error error;
    
        public Result(Object result) { ... }
    
        public Result(Exception e) {
            this.result = null;
            this.error = new Error(e);
        }
    }
    

    And the on the other, call result.error.toThrowable().

    public static class Error {
        public final String message;
        public final List<STE> stackTrace;
        public final Error cause;
    
        public Error(Throwable e) {
            message = e.getMessage();
            stackTrace = Arrays.stream(e.getStackTrace()).map(STE::new).collect(Collectors.toList());
            cause = e.getCause() != null ? new Error(e.getCause()) : null;
        }
    
        public Throwable toThrowable() {
            Throwable t = new Throwable(message);
            t.setStackTrace(stackTrace.stream().map(STE::toStackTraceElement).toArray(StackTraceElement[]::new));
            if (cause != null) {
                t.initCause(cause.toThrowable());
            }
            return t;
        }
    
        private static class STE {
            public final String declaringClass;
            public final String methodName;
            public final String fileName;
            public final int    lineNumber;
    
            public STE(StackTraceElement ste) {
                this.declaringClass = ste.getClassName();
                this.methodName = ste.getMethodName();
                this.fileName = ste.getFileName();
                this.lineNumber = ste.getLineNumber();
            }
    
            public StackTraceElement toStackTraceElement() {
                return new StackTraceElement(declaringClass, methodName, fileName, lineNumber);
            }
        }
    }