我正在尝试创建一个类,其中将 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
因此这两个共享一些代码如何创建和反序列化 POJO
。 Exception
不是常规的 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/