【问题标题】:Serialize only compile-time information仅序列化编译时信息
【发布时间】:2019-03-28 20:11:50
【问题描述】:

在序列化 Java 对象时,由于它利用反射,Jackson 会在其运行时表示中序列化对象。

我有以下课程:

@lombok.AllArgsConstructor
@lombokGetter
class ErrorInformation {

   private final Exception exception;
}

final ErrorInformation errorInfo = new ErrorInformation(new IllegalArgumentException("foo"));

Jackson 不只是序列化 Exception,而是实际上序列化了 IllegalArgumentException 中的所有字段 - 会产生无法控制的后果,例如循环引用(参见 org.springframework.web.client.HttpServerErrorException 中的 mostSpecificCause)。

我怎样才能告诉 Jackson 实际上只序列化 Exception 的信息,而不考虑实际的运行时类型,就像 Java 这样的静态类型语言所期望的那样?

【问题讨论】:

  • 这就是Jackson 的工作方式——找到所有getters 并尝试序列化为JSON。这是默认行为。如果您不想看到此对象的所有额外详细信息,您需要指示 Jackson 隐藏什么以及可以序列化什么。您期望的结果是什么:只有异常消息?
  • @MichałZiober 我知道杰克逊就是这样工作的。但这并不能证明它以这种方式工作是合理的。我想要的是完全序列化Exception 类的所有字段——因为这就是我作为程序员在这种情况下要处理的问题。如果我需要一个特定的类,我会改用泛型。
  • “一些疯子使用反射构建了整个库”对于像 Jackson 这样您可以免费使用的超级有用的库来说,这并不是什么好话。
  • @SebastiaanvandenBroek 你说得对,我的错。我编辑了我的问题。虽然我仍然认为这不是正确的方法。

标签: java jackson


【解决方案1】:

Jackson 默认序列化给定POJO 中的所有getters 方法。如果我们想序列化3-rd party 类,我们需要注意循环和其他问题。为此,我们可以使用MixIn feature。我们需要为所有要忽略的 NestedRuntimeException 异常定义 getMostSpecificCause 方法。

interface NestedRuntimeExceptionMixIn {

    @JsonIgnore
    Throwable getMostSpecificCause();
}

示例用法:

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.core.NestedRuntimeException;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.HttpServerErrorException;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        HttpServerErrorException exception = new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "error");
        ErrorInformation ei = new ErrorInformation(exception);

        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.addMixIn(NestedRuntimeException.class, NestedRuntimeExceptionMixIn.class);

        System.out.println(mapper.writeValueAsString(ei));
    }
}

打印:

{
  "exception" : {
    "cause" : null,
    "stackTrace" : [ {
      "methodName" : "main",
      "fileName" : "JsonApp.java",
      "lineNumber" : 13,
      "className" : "JsonApp",
      "nativeMethod" : false
    } ],
    "rawStatusCode" : 500,
    "statusText" : "error",
    "responseHeaders" : null,
    "statusCode" : "INTERNAL_SERVER_ERROR",
    "responseBodyAsByteArray" : "",
    "responseBodyAsString" : "",
    "message" : "500 error",
    "rootCause" : null,
    "localizedMessage" : "500 error",
    "suppressed" : [ ]
  }
}

【讨论】:

  • 有点像黑名单?
  • @MarkusAppel,没错。你需要明确告诉Jackson 不要看这个getter
  • 那么它没有回答我的问题。
  • 是的,这是我认为的唯一方法。请参阅@Ashishkumar Singh 的回答。
  • @MarkusAppel,我看到了。所以,您确实创建了一个简单的POJO,只需要getters。这是一个经典的serialisation/deserialisation 问题如何使用不正常的3-rd 派对类POJO-s。
【解决方案2】:

由于我们无法更改 IllegalArgumentException 类逻辑,您可以在您的 ErrorInformation 类中创建额外的实例字段,使用您想要序列化的信息填充它并将 exception 变量标记为 transient

例如假设您要保存错误消息信息。因此,创建一个String 字段,例如errorMessage,并在构造函数中填充错误消息信息。下面是一个示例代码。

@lombok.AllArgsConstructor
@lombokGetter
class ErrorInformation {

   private final transient Exception exception;
   private final String errorMessage ;
   ErrorInformation(Exception exp) {
    this.exception = exp;
    this.errorMessage = exp.getMessage();
    }

}

在反序列化的时候,你可以使用这个错误信息信息来创建一个被序列化的异常对象的相似(不一样)实例

【讨论】:

  • 是的,这就是我最终所做的。谢谢。
猜你喜欢
  • 2016-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多