使用Jackson进行没有堆栈跟踪的序列化/反序列化异常 [英] Serializing/deserializing exceptions without stack trace using Jackson
问题描述
我正在尝试使用存储为字段的java.lang.Exception
创建一个类.另外,我正在尝试使用@JsonIgnoreProperties
批注从堆栈化/反序列化中排除堆栈跟踪.
I am trying to create a class with a java.lang.Exception
stored as a field. Also I am trying to exclude stack trace from serialization/deserialization using @JsonIgnoreProperties
annotation.
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);
}
}
结果错误非常令人惊讶,杰克逊找不到message
字段:
The result error is quite surprising, Jackson cannot find the message
field:
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"
添加到忽略的字段也可以使其正常工作(但没有异常消息).
Well, there is only getMessage
and no setMessage
in Throwable
class so it seems a bit reasonable unless you try to remove @JsonIgnoreProperties
annotation. It works like a charm: it serializes and deserializes back properly and missing setter for message
is suddenly not a problem. Adding "message"
to ignored fields also make it work (yet without an exception message).
我尝试使用调试器随机插入Jackson代码,发现缺少@JsonIgnoreProperties
时,最终会调用ThrowableDeserializer
方法,而在存在注释时不会调用它们. ThrowableDeserializer
似乎有一些针对异常消息的技巧.我的猜测是,当缺少堆栈跟踪并且Jackson退回到默认的Java Bean序列化程序时,ThrowableDeserializer
不可行.
I tried to randomly step in Jackson code using debugger and found out that when @JsonIgnoreProperties
is missing, eventually ThrowableDeserializer
methods are called and they are not called when the annotation is present. ThrowableDeserializer
seems to have some hacks specific to exception message. My guess is that ThrowableDeserializer
is not viable when the stack trace is missing and Jackson falls back to default java bean serializer.
问题是这里到底发生了什么以及如何解决.
The question is what exactly is going on here and how to solve it.
推荐答案
版本2
ThrowableDeserializer
类扩展了BeanDeserializer
,因此这两个共享一些代码来创建和反序列化POJO
. Exception
不是常规的POJO
,应以不同的方式处理.由于它没有提供给很多设置器,因此我们需要使用构造函数来创建带有消息和其他可以跳过的字段的设置器.要注册构造函数,我们可以使用MixIn
功能:
Version 2
ThrowableDeserializer
class extends BeanDeserializer
so these two share some code how to create and deserialise a POJO
. Exception
is not a regular POJO
and should be handled differently. Since it does not provide to many setters we need to use constructor to create it with message and other fields we can skip. To register constructor we can use MixIn
feature:
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
的以上代码,但有异常原因打印:
Above code for a JSON
with exception with a cause prints:
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
.
As you can see, exception has default stackTrace
and stackTrace
from JSON
payload is skipped.
我并没有进行过多的研究,但是启用allowSetters
可以解决此问题:
I did not dig it too much but enabling allowSetters
solves this problem:
@JsonIgnoreProperties(value = {"stackTrace"}, allowSetters = true)
public Exception ex;
您的带有此更改的代码会打印:
Your code with this change prints:
{"ex":{"cause":null,"message":"Oops","localizedMessage":"Oops","suppressed":[]}}
ExWrapper{ex=java.lang.Exception: Oops}
这篇关于使用Jackson进行没有堆栈跟踪的序列化/反序列化异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!