I'm using FastJSON to serialize and de-serialize some classes. However, I've found a combination that doesn't work: An exception that contains holds a reference to an object from another class cannot be de-serialized. Example:
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
public class Main {
@AllArgsConstructor
@Getter
@Setter
private static class A {
private String a;
private String b;
}
@AllArgsConstructor
@Getter
@Setter
private static class E extends Exception {
private A a;
}
public static void main(String[] args) {
E original = new E(new A("hello", "world"));
String serialized = JSON.toJSONString(original);
E deserialized = JSON.parseObject(serialized, E.class); // throws below exception
}
}
This example yields the following runtime error:
Exception in thread "main" com.alibaba.fastjson.JSONException: set property error, com.example.Main$E#a
at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:183)
at com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer.deserialze(ThrowableDeserializer.java:149)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:688)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:396)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:300)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:573)
at com.example.Main.main(Main2.java:40)
Caused by: java.lang.IllegalArgumentException: Can not set com.example.Main$A field com.example.Main$E.a to java.lang.Exception
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.lang.reflect.Field.set(Field.java:764)
at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:178)
... 6 more
It tells me that it cannot set the field a
in E
to an Exception
object. It looks like a bug in FastJSON because I'd expect it to de-serialize the value of a
to an object of A
, not E
. Or did I made a mistake? What causes this problem and what has to be changed in order to circumvent this?
When de-serializing a throwable, FastJSON first instantiates the throwable object, then sets its attributes. To instantiate the object, it looks for three different constructors:
If none of these is available, it defaults to instantiating an
Exception
object.However, I defined none of the above constructors. Hence, FastJSON instantiated an
Exception
object. Later, it tried to set the attribute.a
of this exception object to the reference of theA
instance. This doesn't work becauseException
has no such attribute.Furthermore, my internal classes were declared private, so FastJSON cannot access them.
The solution is simple: