使用Jackson进行没有堆栈跟踪的序列化/反序列化异常 [英] Serializing/deserializing exceptions without stack trace using Jackson

查看:127
本文介绍了使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆