Java项目中有用Fastjson2序列化一个map的代码,当map中有一个自定义的InvalidParameterException对象时会StackOverflowError。
简化一下如下:
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.x.x.x.x.InvalidParameterException;
import org.junit.jupiter.api.Test;public class JsonErrorTest {/*** 测试Fastjson2*/@Testpublic void fastjson2Test() {InvalidParameterException exception = new InvalidParameterException("test");JSON.toJSONString(exception);}/*** 测试Jackson*/@Testpublic void jacksonTest() throws JsonProcessingException {InvalidParameterException exception = new InvalidParameterException("test");ObjectMapper objectMapper = new ObjectMapper();objectMapper.writeValueAsString(exception);}
}
测试Fastjson2结果如下:
java.lang.StackOverflowErrorat com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:205)at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)at com.alibaba.fastjson2.writer.ObjectWriterException.write(ObjectWriterException.java:45)at com.alibaba.fastjson2.writer.FieldWriterObject.write(FieldWriterObject.java:327)
看到这个报错一开始不知道是什么原因。
测试Jackson结果如下:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: com.x.x.x.x.InvalidParameterException["rootCause"])at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1277)at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._handleSelfReference(BeanPropertyWriter.java:945)at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:722)at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755)at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4407)at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3661)
可以看到Jackson的报错提示很清晰:com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: com.x.x.x.x.InvalidParameterException["rootCause"])
是“rootCause”字段引用自己(this)导致的循环引用。通过查看代码,异常类中的确有这样一段代码
public Throwable getRootCause() {Throwable rootCause = this;Throwable cause = getCause();while (cause != null && cause != rootCause) {rootCause = cause;cause = cause.getCause();}return rootCause;}
这是一个get方法,json框架会序列化为rootCause字段,这个方法有可能返回自身,所以造成了循环引用。
解决方法:
1.(推荐)修改类代码,不要循环引用。
2.(不推荐)对于Fastjson2使用框架的检测循环引用的参数来避免报错,代码如下:
/*** 测试Fastjson2*/@Testpublic void fastjson2Test() {InvalidParameterException exception = new InvalidParameterException("test");JSON.toJSONString(exception, JSONWriter.Feature.ReferenceDetection);}
对于Jackson,可以在循环引用字段上加上这些注解@JsonIgnore, @JsonBackReference