使用Java中的Jackson反序列化异常/抛出时的问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Java中的Jackson反序列化异常/抛出时的问题相关的知识,希望对你有一定的参考价值。
我在使用Jackson(版本2.2.1)反序列化Exception
和Throwable
实例时遇到问题。请考虑以下代码段:
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();
}
}
System.out.println
区块中catch
的输出是:
{
"@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,忽略declaringClass
中的java.lang.StackTraceElement
,但现在反序列化的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似乎有杰克逊JIRA条目。杰克逊似乎无法处理declaringClass
中的java.lang.StackTraceElement
,因为对应于这个领域的吸气剂被称为getClassName()
。
我修复了这个问题,使用StackTraceElement
周围的自定义包装器,如上面提到的JIRA条目中所建议的那样。自定义包装器(CustomStackTraceElement
)将包含字段declaringClass
,methodName
,fileName
和lineNumber
以及相应的getter和setter。我修改了catch
块(在问题中提到)如下:
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
对象在其中具有预期的堆栈跟踪。
添加这个:
objectMapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
并以同样的方式从反序列化的异常中做出,这是第一次:
System.out.println( objectMapper.writeValueAsString( throwable ) );
我使用了以下代码:
public static void main( String[] args ) throws IOException
{
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure( SerializationFeature.INDENT_OUTPUT, true );
objectMapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY );
objectMapper.enableDefaultTyping( ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY );
try
{
Integer.parseInt( "String" );
}
catch( NumberFormatException e )
{
Throwable throwable = objectMapper.readValue( objectMapper.writeValueAsString( e ), Throwable.class );
System.out.println( objectMapper.writeValueAsString( throwable ) );
}
}
添加了这个罐子:jackson-annotations-2.2.0.jar,jackson-core-2.2.0.jar和jackson-databind-2.2.0.jar。
执行后,打印以下内容:
{
"@class" : "java.lang.NumberFormatException",
"detailMessage" : "For input string: "String"",
"cause" : null,
"stackTrace" : [ {
"declaringClass" : "java.lang.NumberFormatException",
"methodName" : "forInputString",
"fileName" : "NumberFormatException.java",
"lineNumber" : 48,
"className" : "java.lang.NumberFormatException",
"nativeMethod" : false
}, {
"declaringClass" : "java.lang.Integer",
"methodName" : "parseInt",
"fileName" : "Integer.java",
"lineNumber" : 449,
"className" : "java.lang.Integer",
"nativeMethod" : false
}, {
"declaringClass" : "java.lang.Integer",
"methodName" : "parseInt",
"fileName" : "Integer.java",
"lineNumber" : 499,
"className" : "java.lang.Integer",
"nativeMethod" : false
}, {
"declaringClass" : "com.sample.bla.Main",
"methodName" : "main",
"fileName" : "Main.java",
"lineNumber" : 24,
"className" : "com.sample.bla.Main",
"nativeMethod" : false
}, {
"declaringClass" : "sun.reflect.NativeMethodAccessorImpl",
"methodName" : "invoke0",
"fileName" : "NativeMethodAccessorImpl.java",
"lineNumber" : -2,
"className" : "sun.reflect.NativeMethodAccessorImpl",
"nativeMethod" : true
}, {
"declaringClass" : "sun.reflect.NativeMethodAccessorImpl",
"methodName" : "invoke",
"fileName" : "NativeMethodAccessorImpl.java",
"lineNumber" : 39,
"className" : "sun.reflect.NativeMethodAccessorImpl",
"nativeMethod" : false
}, {
"declaringClass" : "sun.reflect.DelegatingMethodAccessorImpl",
"methodName" : "invoke",
"fileName" : "DelegatingMethodAccessorImpl.java",
"lineNumber" : 25,
"className" : "sun.reflect.DelegatingMethodAccessorImpl",
"nativeMethod" : false
}, {
"declaringClass" : "java.lang.reflect.Method",
"methodName" : "invoke",
"fileName" : "Method.java",
"lineNumber" : 597,
"className" : "java.lang.reflect.Method",
"nativeMethod" : false
}, {
"declaringClass" : "com.intellij.rt.execution.application.AppMain",
"methodName" : "main",
"fileName" : "AppMain.java",
"lineNumber" : 120,
"className" : "com.intellij.rt.execution.application.AppMain",
"nativeMethod" : false
} ],
"message" : "For input string: "String"",
"localizedMessage" : "For input string: "String""
}
您在版本2.2.1中获得的输出似乎与我在2.2.0版本中获得的输出不同(根据该网站是最新的2.x版本)。除了Maven Repository上最新的2.x版本是2.2.2。所以我会尝试将其降级到2.2.0或将其升级到2.2.2。如果任何更改带来预期结果,我会更进一步使用该版本并在Jackson的JIRA中打开一个BUG。
当然不要忘了
objectMapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
来自Michael的回答。
我有类似的问题。我现在正在使用这个代码,它允许我使用适当的类型序列化和反序列化异常(即RuntimeException
将再次成为RuntimeException
:)):
public static ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper(null, null, new DefaultDeserializationContext.Impl(
new BeanDeserializerFactory(new DeserializerFactoryConfig()) {
private static final long serialVersionUID = 1L;
@Override
public JsonDeserializer<Obj以上是关于使用Java中的Jackson反序列化异常/抛出时的问题的主要内容,如果未能解决你的问题,请参考以下文章
Jackson ObjectMapper 无法反序列化 POJO,抛出异常:没有找到适合类型 [...] 的构造函数:无法从 JSON 对象实例化
理解异常的麻烦:“无法从 START_OBJECT 令牌中反序列化 `java.lang.String` 的实例”在 Jackson 中使用 ObjectMapper
如何使用jackson(Java)反序列化对象中的json对象?