java - 使用 Jackson 在没有堆栈跟踪的情况下序列化/反序列化异常

标签 java json jackson

我正在尝试创建一个类,其中将 java.lang.Exception 存储为一个字段。此外,我正在尝试使用 @JsonIgnoreProperties 注释从序列化/反序列化中排除堆栈跟踪。

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

class ExWrapper {
    @JsonIgnoreProperties({"stackTrace"})
    public Exception ex;

    @Override
    public String toString() {
        return "ExWrapper{" +
                "ex=" + ex +
                '}';
    }
}

public class Example {
    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        ExWrapper exw = new ExWrapper();
        exw.ex = new Exception("Oops");
        String str = mapper.writeValueAsString(exw);
        System.out.println(str);
        ExWrapper exW = mapper.readValue(str, ExWrapper.class);
        System.out.println(exW);
    }
}

结果错误比较意外,Jackson找不到message字段:

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "message" (class java.lang.Exception), not marked as ignorable (one known property: "cause"])
 at [Source: (String)"{"ex":{"cause":null,"message":"Oops","suppressed":[],"localizedMessage":"Oops"}}"; line: 1, column: 32] (through reference chain: ExWrapper["ex"]->java.lang.Exception["message"])
    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:840)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1179)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1592)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1570)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:375)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3205)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3173)
    at Example.main(Example.java:25)

嗯,Throwable 类中只有 getMessage 而没有 setMessage 所以这似乎有点合理,除非你尝试删除 @JsonIgnoreProperties 注解。它就像一个魅力:它正确地序列化和反序列化并且缺少 message 的 setter 突然不是问题。将 "message" 添加到忽略的字段也可以使其工作(但没有异常消息)。

我尝试使用调试器随机介入 Jackson 代码,发现当 @JsonIgnoreProperties 丢失时,最终调用了 ThrowableDeserializer 方法,但在注释时未调用它们存在。 ThrowableDeserializer 似乎有一些特定于异常消息的技巧。我的猜测是 ThrowableDeserializer 在堆栈跟踪丢失且 Jackson 回退到默认 java bean 序列化器时不可行。

问题是这里究竟发生了什么以及如何解决它。

最佳答案

版本 2

ThrowableDeserializer 类扩展了 BeanDeserializer 因此这两个共享一些代码如何创建和反序列化 POJOException 不是常规的 POJO,应该以不同的方式处理。由于它没有提供给许多 setter ,我们需要使用构造函数来创建它,其中包含消息和我们可以跳过的其他字段。要注册构造函数,我们可以使用 MixIn 功能:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;

public class JsonPathApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        ObjectMapper mapper = new ObjectMapper();
        mapper.addMixIn(Exception.class, ExceptionMixIn.class);
        mapper.addMixIn(Throwable.class, ThrowableMixIn.class);

        ExWrapper exW = mapper.readValue(jsonFile, ExWrapper.class);
        exW.ex.printStackTrace();
    }
}

@JsonIgnoreProperties("stackTrace")
abstract class ExceptionMixIn extends Exception {

    @JsonCreator
    public ExceptionMixIn(@JsonProperty("message") String message) {
        super(message);
    }
}

@JsonIgnoreProperties("stackTrace")
abstract class ThrowableMixIn extends Throwable {

    @JsonCreator
    public ThrowableMixIn(@JsonProperty("message") String message) {
        super(message);
    }
}

class ExWrapper {

    public Exception ex;

    @Override
    public String toString() {
        return "ExWrapper{" +
                "ex=" + ex +
                '}';
    }
}

上面的 JSON 代码,异常原因打印:

java.lang.Exception: Opps
    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:422)
    at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:124)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:283)
    at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:229)
    at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:195)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:422)
    at com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer.deserializeFromObject(ThrowableDeserializer.java:65)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2902)
Caused by: java.lang.Throwable: Root oops
    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:422)
    at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:124)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:283)
    at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:229)
    at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:195)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:422)
    at com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer.deserializeFromObject(ThrowableDeserializer.java:65)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:530)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:528)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:449)
    ... 8 more

如您所见,异常具有默认的 stackTrace 和来自 JSON 有效负载的 stackTrace 被跳过。

版本 1

我没有挖掘太多,但启用 allowSetters 解决了这个问题:

@JsonIgnoreProperties(value = {"stackTrace"}, allowSetters = true)
public Exception ex;

具有此更改的您的代码打印:

{"ex":{"cause":null,"message":"Oops","localizedMessage":"Oops","suppressed":[]}}
ExWrapper{ex=java.lang.Exception: Oops}

关于java - 使用 Jackson 在没有堆栈跟踪的情况下序列化/反序列化异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58530206/

相关文章:

java - 使用 java.security.KeyStore 将椭圆曲线证书和私钥导入 Java Keystore

javascript - 动态计算 json 行数

java - @JsonSerialize 和 @JsonDeserialize 包含在注释中时不起作用

java - @class 被添加,即使它是 defaultImpl

java - Java for循环中的return语句不退出方法

java - Java 的最佳缓存框架

java - 是否可以在没有条件语句的情况下创建具有相同父类的随机对象?

ios - 显示播客列表中的剧集列表

c# - 序列化为 JSON(使用 Json.Net),同时忽略继承的成员

java - 解析传入的 JSON 并删除包装样板