對象轉 JSON 可能引發棧溢出的異常,一般是因為對象中的循環引用引起不斷遞歸。
常見的作法就是:
- 換一種 JSON 的序列化工具,比如?fastjson 默認支持消除對同一對象循環引用
- transient 修飾屬性
- 顯式排除對象的某些屬性
?
1. java對象引用成環說明
1.1 相互引用成環:
class A{B b;}class B{A a;}
?
1.2 自引用成環:
class A{A a;}
2. 引用成環造成的問題
在java中,對象引用成環問題,可以被jvm自動處理,但是將java對象轉成json格式時,由于轉換工具不能自行切斷環,會導致無限解析最終會導致棧溢出錯誤。
?
3. 解決方法:
?所有解決方法,基本原理都是將“環”切斷。
1)gson提供了非常簡潔的處理方式,將所有要轉換成json的變量都用@Expose注解標記;將出現環的地方,不做任何處理。
2)創建gson構造器:
?
// 獲取Gson構造器,可以過濾掉不帶注解的字段 Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
?
?
3)轉換json:
?
gson.toJson(testOject);
?
使用上面第一個相互引用成環的例子舉例說明:
3.1 阻斷環路:
?
class A{@ExposeB b; }class B{A a;//不轉換該字段,阻斷循環引用 }
?
3.2 創建gson構造器,并轉換json:
?
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();// 獲取Gson構造器,可以過濾掉不帶注解的字段 A testObj = new A();String json = gson.toJson(testObj);//獲取json數據
Gson 官方文檔
Excluding Fields From Serialization and Deserialization
Gson supports numerous mechanisms for excluding top-level classes, fields and field types. Below are pluggable mechanisms that allow field and class exclusion. If none of the below mechanisms satisfy your needs then you can always use?custom serializers and deserializers.
Java Modifier Exclusion
By default, if you mark a field as?transient, it will be excluded. As well, if a field is marked as?static?then by default it will be excluded. If you want to include some transient fields then you can do the following:
import java.lang.reflect.Modifier;
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.STATIC).create();
?
NOTE: you can give any number of the?Modifier?constants to the?excludeFieldsWithModifiers?method. For example:
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE).create();
?
Gson's?@Expose
This feature provides a way where you can mark certain fields of your objects to be excluded for consideration for serialization and deserialization to JSON. To use this annotation, you must create Gson by using?new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(). The Gson instance created will exclude all fields in a class that are not marked with?@Expose?annotation.
User Defined Exclusion Strategies
If the above mechanisms for excluding fields and class type do not work for you then you can always write your own exclusion strategy and plug it into Gson. See the?ExclusionStrategy?JavaDoc for more information.
The following example shows how to exclude fields marked with a specific?@Foo?annotation and excludes top-level types (or declared field type) of class?String.
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD})public @interface Foo {// Field tag only annotation }public class SampleObjectForTest {@Foo private final int annotatedField;private final String stringField;private final long longField;private final Class<?> clazzField;public SampleObjectForTest() {annotatedField = 5;stringField = "someDefaultValue";longField = 1234;}}public class MyExclusionStrategy implements ExclusionStrategy {private final Class<?> typeToSkip;private MyExclusionStrategy(Class<?> typeToSkip) {this.typeToSkip = typeToSkip;}public boolean shouldSkipClass(Class<?> clazz) {return (clazz == typeToSkip);}public boolean shouldSkipField(FieldAttributes f) {return f.getAnnotation(Foo.class) != null;}}public static void main(String[] args) {Gson gson = new GsonBuilder().setExclusionStrategies(new MyExclusionStrategy(String.class)).serializeNulls().create();SampleObjectForTest src = new SampleObjectForTest();String json = gson.toJson(src);System.out.println(json);}
?
The output is:
{"longField":1234}
?
?
References
https://blog.csdn.net/weixin_33712987/article/details/87500527
https://my.oschina.net/airship/blog/792414
https://github.com/google/gson/blob/master/UserGuide.md#TOC-Excluding-Fields-From-Serialization-and-Deserialization
?