在 Java 中使用 Jackson 反序列化异常/抛出的问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17855538/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Issues while deserializing exception/throwable using Hymanson in Java
提问by Hymanall
I am facing issues while deserializing Exception
and Throwable
instances using Hymanson (version 2.2.1). Consider the following snippet:
我在使用 Hymanson(版本 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();
}
}
The output of System.out.println
in the catch
block is:
的输出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.Hymanson.HymansonTest",
"methodName" : "main",
"fileName" : "HymansonTest.java",
"lineNumber" : 26
} ],
"suppressedExceptions" : [ "java.util.ArrayList", [ ] ]
},
"stackTrace" : [ {
"declaringClass" : "test.Hymanson.HymansonTest",
"methodName" : "main",
"fileName" : "HymansonTest.java",
"lineNumber" : 29
} ],
"suppressedExceptions" : [ "java.util.ArrayList", [ ] ]
}
which seems fine. But when I attempt to deserialize this using objectMapper.readValue()
, I get the following exception:
这似乎很好。但是当我尝试使用 反序列化它时objectMapper.readValue()
,我得到以下异常:
Exception in thread "main" com.fasterxml.Hymanson.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.Hymanson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)
at com.fasterxml.Hymanson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:555)
at com.fasterxml.Hymanson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:708)
at com.fasterxml.Hymanson.databind.deser.std.JdkDeserializers$StackTraceElementDeserializer.deserialize(JdkDeserializers.java:414)
at com.fasterxml.Hymanson.databind.deser.std.JdkDeserializers$StackTraceElementDeserializer.deserialize(JdkDeserializers.java:380)
at com.fasterxml.Hymanson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:151)
...
I then tried using mix-in annotations, to ignore declaringClass
in java.lang.StackTraceElement
, but now the deserialized Exception
doesn't contain the declaring class in its stack trace:
然后我尝试使用混合注释来忽略declaringClass
in java.lang.StackTraceElement
,但现在反序列化Exception
的堆栈跟踪中不包含声明类:
java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "String"
at .main(HymansonTest.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(HymansonTest.java:30)
Am I missing anything? Any help is greatly appreciated.
我错过了什么吗?任何帮助是极大的赞赏。
采纳答案by Hymanall
There seems to be a Hymanson JIRA entry for this here. Hymanson doesn't seem to be able to handle the declaringClass
in java.lang.StackTraceElement
, since the getter corresponding to this field is called getClassName()
.
似乎有此Hyman逊JIRA进入这里。Hymanson 似乎无法处理declaringClass
in java.lang.StackTraceElement
,因为与该字段对应的 getter 被称为getClassName()
。
I fixed this issue by using a custom wrapper around StackTraceElement
as suggested in the JIRA entry mentioned above. The custom wrapper (CustomStackTraceElement
) will have the fields declaringClass
, methodName
, fileName
, and lineNumber
and the corresponding getters and setters in it. I modified the catch
block (mentioned in the question) to be as follows:
我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();
}
The StackTraceElement[]
will be converted into List<CustomStackTraceElement>
by the following method during serialization:
所述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;
}
... and the reverse transformation will be done during deserialization:
...并且在反序列化期间将进行反向转换:
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;
}
Now, after deserialization, the Throwable
object has the expected stack trace in it.
现在,反序列化后,该Throwable
对象中包含预期的堆栈跟踪。
回答by Michael Cheremuhin
Add this:
添加这个:
objectMapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
And make out of the deserialized exception the same way, as for the first time:
并以与第一次相同的方式解决反序列化异常:
System.out.println( objectMapper.writeValueAsString( throwable ) );
I used the following code:
我使用了以下代码:
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 ) );
}
}
Added this jars: Hymanson-annotations-2.2.0.jar, Hymanson-core-2.2.0.jar and Hymanson-databind-2.2.0.jar.
添加了这个 jars:Hymanson-annotations-2.2.0.jar、Hymanson-core-2.2.0.jar 和 Hymanson-databind-2.2.0.jar。
After execution, the following is printed:
执行后打印如下:
{
"@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\""
}
回答by Andrei I
It seems that the output you get in version 2.2.1 is not the same as I get with version 2.2.0 (which according to the website is the latest 2.x version). Besides the latest available 2.x version on the Maven Repository is 2.2.2. So I would try to either downgrade it to 2.2.0 or to upgrade it to 2.2.2. If any of the changes brings you the expected result, I would go further with that version and open a BUG in Hymanson's JIRA.
您在 2.2.1 版中获得的输出似乎与我在 2.2.0 版中获得的输出不同(根据网站,这是最新的 2.x 版)。除了 Maven 存储库上最新的可用 2.x 版本是 2.2.2。所以我会尝试将其降级到 2.2.0 或升级到 2.2.2。如果任何更改为您带来了预期的结果,我会进一步使用该版本并在 Hymanson 的 JIRA 中打开一个 BUG。
And of course don't forget
当然不要忘记
objectMapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
from Michael's answer.
来自迈克尔的回答。
回答by Markus Duft
I've had a similar issue. I'm using this code now, and it allows me to serialize and deserialize exceptions with proper types (i.e. a RuntimeException
will be a RuntimeException
again :)):
我有过类似的问题。我现在正在使用此代码,它允许我使用正确的类型序列化和反序列化异常(即 aRuntimeException
将RuntimeException
再次成为 a :)):
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<Object> buildThrowableDeserializer(
DeserializationContext ctxt, JavaType type, BeanDescription beanDesc)
throws JsonMappingException {
return super.buildBeanDeserializer(ctxt, type, beanDesc);
}
}));
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.addMixIn(Throwable.class, ThrowableMixin.class);
mapper.addMixIn(StackTraceElement.class, StackTraceElementMixin.class);
return mapper;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonIgnoreProperties({ "message", "localizedMessage", "suppressed" })
abstract class ThrowableMixin {
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "$id")
private Throwable cause;
}
abstract class StackTraceElementMixin {
@JsonProperty("className")
private String declaringClass;
}
I'm manipulating the BeanDeserializerFactory
to make buildThrowableDeserializer
not treat Throwable
any special but just like any other Object
. Then using Mixins
to define the "special" handling of Throwable
and StackTraceElement
to my liking.
我正在操纵BeanDeserializerFactory
使buildThrowableDeserializer
不处理Throwable
任何特殊但就像任何其他Object
. 然后,使用Mixins
来定义“特殊”处理的Throwable
和StackTraceElement
我的胃口。
回答by Chas
Try using polymorphism so that Hymanson deserializer knows what kind of Throwable to create:
尝试使用多态,以便 Hymanson 反序列化器知道要创建什么样的 Throwable:
/**
* Hymanson module to serialize / deserialize Throwable
*/
public class ThrowableModule extends SimpleModule {
public ThrowableModule() {
super("Throwable", new Version(1, 0, 0, null, null, null));
}
@Override
public void setupModule(SetupContext context) {
context.setMixInAnnotations(Throwable.class, ThrowableAnnotations.class);
}
/**
* Add annotation to Throwable so that the class name is serialized with the instance data.
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class")
static abstract class ThrowableAnnotations {
}
}
回答by Michael Cheremuhin
Is it so necessary to use json serialization? Looks liks there are some bugs with throwables. Why not use system api:
有那么必要用json序列化吗?看起来像 throwables 有一些错误。为什么不使用系统api:
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream( );
ObjectOutputStream objectOutputStream = new ObjectOutputStream( byteArrayOutputStream );
objectOutputStream.writeObject( e );
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( byteArrayOutputStream.toByteArray() );
ObjectInputStream objectInputStream = new ObjectInputStream( byteArrayInputStream );
Throwable t = (Throwable) objectInputStream.readObject();