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
Exceptionobject.However, I defined none of the above constructors. Hence, FastJSON instantiated an
Exceptionobject. Later, it tried to set the attribute.aof this exception object to the reference of theAinstance. This doesn't work becauseExceptionhas no such attribute.Furthermore, my internal classes were declared private, so FastJSON cannot access them.
The solution is simple: