Java 17中GSON无法序列化异常

8
以下代码曾经在 Java 11 上可用:
new Gson().toJson(new Exception())

在JDK 17上,我遇到了以下错误:
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

从阅读这个页面,我认为我可以通过--add-opens java.base/java.lang=ALL-UNNAMED来解决它。但是是否有更好的方法呢?也许使用自定义的de/serializer会更好?


1
为什么要序列化一个类,其结构可能会在不同版本之间或不同JDK/JRE实现之间发生变化,从而完全破坏您的设计?您根本无法控制它。 Gson通过设计对于它不知道的对象(未注册特殊类型适配器)使用反射,因此它只是迭代所有对象字段,无论它们是公共的还是私有的。外部库的私有内容永远不应该像这样被序列化。绝不。 - terrorrussia-keeps-killing
我们有两个服务。当A调用B时,B可能会抛出异常。我将其序列化并传回给A,以便A可以将其作为所抛出的异常的原因包含在内。这对于调查崩溃非常有用;我没有意识到这是一个大忌。 - Malcolm Crum
3
我理解你的目的,但你需要自己创建数据传输对象(DTO)来承载异常数据,这样你就可以完全控制DTOs并决定应该从异常中获取哪些数据,因为异常可能有比我想象的更多的私有字段(私有字段、包私有或私有类、各种引用或循环引用等)。这将需要一些代码来将异常映射到DTO(如果需要,还需要将其转换回异常),但是使用“Throwable”的公共API时,你就永远不会遇到这样的问题。 - terrorrussia-keeps-killing
3个回答

3
我昨天遇到了这个问题。当时我在使用Java 17,后来切换回Java 11就没问题了。
我认为问题出在这里:https://bugs.openjdk.java.net/browse/JDK-8256358 我有点懒,所以一直使用GSON默认的反射类型适配器。
你需要自己实现一个TypeAdapter来解决这个问题。或者也可以考虑使用另外一个JSON反序列化库,比如Jackson,我可能会在之后尝试一下。

0
这是我添加到de/serialize异常的代码。可以在类中使用,像这样:
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);
    }
}

而在另一方面,调用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);
        }
    }
}

-3

那并没有帮助解决这个问题。 - user3892260

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接