java - 在 Java 中使用 Jackson 反序列化异常/throwable 时出现问题

标签 java jackson deserialization

我在使用 Jackson(版本 2.2.1)反序列化 ExceptionThrowable 实例时遇到问题。考虑以下代码段:

public static void main(String[] args) throws IOException
{
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
    objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
    objectMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);

    try {
        Integer.parseInt("String");
    }
    catch (NumberFormatException e) {
        RuntimeException runtimeException = new RuntimeException(e);
        String serializedException = objectMapper.writeValueAsString(runtimeException);
        System.out.println(serializedException);
        Throwable throwable = objectMapper.readValue(serializedException, Throwable.class);
        throwable.printStackTrace();
    }
}

catch block 中System.out.println的输出是:

{
  "@class" : "java.lang.RuntimeException",
  "detailMessage" : "java.lang.NumberFormatException: For input string: \"String\"",
  "cause" : {
    "@class" : "java.lang.NumberFormatException",
    "detailMessage" : "For input string: \"String\"",
    "cause" : null,
    "stackTrace" : [ {
      "declaringClass" : "java.lang.NumberFormatException",
      "methodName" : "forInputString",
      "fileName" : "NumberFormatException.java",
      "lineNumber" : 65
    }, {
      "declaringClass" : "java.lang.Integer",
      "methodName" : "parseInt",
      "fileName" : "Integer.java",
      "lineNumber" : 492
    }, {
      "declaringClass" : "java.lang.Integer",
      "methodName" : "parseInt",
      "fileName" : "Integer.java",
      "lineNumber" : 527
    }, {
      "declaringClass" : "test.jackson.JacksonTest",
      "methodName" : "main",
      "fileName" : "JacksonTest.java",
      "lineNumber" : 26
    } ],
    "suppressedExceptions" : [ "java.util.ArrayList", [ ] ]
  },
  "stackTrace" : [ {
    "declaringClass" : "test.jackson.JacksonTest",
    "methodName" : "main",
    "fileName" : "JacksonTest.java",
    "lineNumber" : 29
  } ],
  "suppressedExceptions" : [ "java.util.ArrayList", [ ] ]
}

这看起来不错。但是,当我尝试使用 objectMapper.readValue() 对其进行反序列化时,出现以下异常:

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "declaringClass" (class java.lang.StackTraceElement), not marked as ignorable
 at [Source: java.io.StringReader@3c5ebd39; line: 9, column: 27] (through reference chain: java.lang.StackTraceElement["declaringClass"])
    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)
    at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:555)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:708)
    at com.fasterxml.jackson.databind.deser.std.JdkDeserializers$StackTraceElementDeserializer.deserialize(JdkDeserializers.java:414)
    at com.fasterxml.jackson.databind.deser.std.JdkDeserializers$StackTraceElementDeserializer.deserialize(JdkDeserializers.java:380)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:151)
...

然后我尝试使用 mix-in annotations , 以忽略 java.lang.StackTraceElement 中的 declaringClass,但现在反序列化的 Exception 在其堆栈跟踪中不包含声明类:

java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "String"
    at .main(JacksonTest.java:33)
Caused by: java.lang.NumberFormatException: For input string: "String"
    at .forInputString(NumberFormatException.java:65)
    at .parseInt(Integer.java:492)
    at .parseInt(Integer.java:527)
    at .main(JacksonTest.java:30)

我错过了什么吗?非常感谢任何帮助。

最佳答案

这个 here 似乎有一个 Jackson JIRA 条目. jackson 似乎无法处理 declaringClassjava.lang.StackTraceElement ,因为这个字段对应的getter叫做getClassName() .

我通过围绕 StackTraceElement 使用自定义包装器解决了这个问题正如上面提到的 JIRA 条目中所建议的那样。自定义包装器 ( CustomStackTraceElement ) 将包含字段 declaringClass , methodName , fileName , 和 lineNumber以及其中相应的getters和setters。我修改了 catch block (在问题中提到)如下:

catch (NumberFormatException e) {
    RuntimeException runtimeException = new RuntimeException(e);
    e.printStackTrace();
    String serializedException = objectMapper.writeValueAsString(runtimeException);
    System.out.println(serializedException);

    String serializedStackTrace = objectMapper.writeValueAsString(transformStackTrace(runtimeException));
    String serializedStackTraceForCause = objectMapper.writeValueAsString(transformStackTrace(runtimeException.getCause()));

    Throwable throwable = objectMapper.readValue(serializedException, Throwable.class);
    List<CustomStackTraceElement> customStackTraceElementList = objectMapper.readValue(serializedStackTrace, List.class);
    List<CustomStackTraceElement> customStackTraceElementListForCause = objectMapper.readValue(serializedStackTraceForCause, List.class);

    throwable.setStackTrace(reverseTransformStackTrace(customStackTraceElementList));
    throwable.getCause().setStackTrace(reverseTransformStackTrace(customStackTraceElementListForCause));
    throwable.printStackTrace();
}

StackTraceElement[]将转换为 List<CustomStackTraceElement>在序列化过程中通过以下方法:

private static List<CustomStackTraceElement> transformStackTrace(Throwable throwable)
{
    List<CustomStackTraceElement> list = new ArrayList<>();
    for (StackTraceElement stackTraceElement : throwable.getStackTrace()) {
        CustomStackTraceElement customStackTraceElement =
            new CustomStackTraceElement(stackTraceElement.getClassName(),
                                        stackTraceElement.getMethodName(),
                                        stackTraceElement.getFileName(),
                                        stackTraceElement.getLineNumber());

        list.add(customStackTraceElement);
    }

    return list;
}

...反序列化时会进行反向转换:

private static StackTraceElement[] reverseTransformStackTrace(List<CustomStackTraceElement> customStackTraceElementList)
{
    StackTraceElement[] stackTraceElementArray = new StackTraceElement[customStackTraceElementList.size()];
    for (int i = 0; i < customStackTraceElementList.size(); i++) {
        CustomStackTraceElement customStackTraceElement = customStackTraceElementList.get(i);
        StackTraceElement stackTraceElement =
            new StackTraceElement(customStackTraceElement.getDeclaringClass(),
                                  customStackTraceElement.getMethodName(),
                                  customStackTraceElement.getFileName(),
                                  customStackTraceElement.getLineNumber());

        stackTraceElementArray[i] = stackTraceElement;
    }

    return stackTraceElementArray;
}

现在,在反序列化之后,Throwable对象中有预期的堆栈跟踪。

关于java - 在 Java 中使用 Jackson 反序列化异常/throwable 时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17855538/

相关文章:

Peek 中的 Java 8 条件逻辑?

java - Tomcat容器中c3p0依赖放在哪里

java - 将带有特殊字符的 JSON 反序列化为字符串

java - 使用 RestTemplate 和 Jackson 反序列化对 Java 的 JSON 响应

java - 是否可以为 iOS 编写应用程序并将 object-c 代码移动到 Android 中?

java - java中快速排序的swap方法

serialization - SignalR : use camel case

serialization - Kotlin-将JSON数组序列化为多个类

java - 如何在 json 数组中将 boolean 值写为字符串?

java - 反序列化 json,将子类对象提升到顶层而不是嵌套类