Java - net.sf.json.JSONException: There is a cycle in the hierarchy!

我們在撰寫相關網頁時,有可能利用到JSONObject或JSONArray來包裝相關的response value

值給前端JavaScript做parse,進而得到相關訊息!當碰到net.sf.json.JSONException: There is a

cycle in the hierarchy!時,有可能是使用JSON上哪裡出了問題?


測試JSON Library版本為json-lib-2.0-jdk15.jar

在這邊撰寫一段簡單的TEST程式

定義一Employees.java及TestJSON.java
  1. public class Employees extends PrimaryBean{
  2. private int employeeid;
  3. private String employeename;
  4. private String department;
  5. private BigDecimal salary;
  6. private Employees self;
  7. //相關setter or getter...略
  8. public boolean equals(Object obj) {
  9. if (obj instanceof Employees) {
  10. Employees compare = (Employees) obj;
  11. return employeeid == compare.getEmployeeid();
  12. } else {
  13. return false;
  14. }
  15. }
  16. public int hashCode() {
  17. return employeeid;
  18. }
  19. }
  20. public class TestJSON {
  21. public static void main(String[] args) {
  22. // TODO Auto-generated method stub
  23. Employees bean = new Employees();
  24. Employees bean2 = new Employees();
  25. bean.setEmployeeid(123);
  26. bean2.setEmployeeid(123);
  27. bean.setSelf(bean2);
  28. JSONObject object = JSONObject.fromObject(bean);
  29. System.out.println(object);
  30. }
  31. }

定義一Employees類別,當new一object時並做相關的set values,此時可透過JSONObject的

fromObject來將Employees轉換成一key, value的JSONObject,key為相關的成員變數,而values

為相關set的值。但以此例會出現net.sf.json.JSONException: There is a cycle in the hierarchy!

的錯誤訊息!

追蹤了一下其source code,發現在JSONObject會去檢查相關特別型態的values值是否存在於

cycleSet內了,若已存在則跳出該Exception!! 以此例會走_fromBean( object );

  1. private static JSONObject _fromBean( Object bean ) {
  2. ...
  3. if( !addInstance( bean ) ){
  4.           try{
  5.             return jsonConfig.getCycleDetectionStrategy()
  6.                   .handleRepeatedReferenceAsObject( bean );
  7.          }
  8.          ......
  9. }
  10. }

=> addInstance由AbstractJSON父類別宣告
  1. protected static boolean addInstance( Object instance ) {
  2.       return cycleSet.add( instance );
  3. }

也就是說它會去作類別是否相等,以Employees來說若宣告了兩個objects,其equals的依據

employeeid皆相同,此時利用cycleSet加入第一次為true(bean本身),若加入第二次則為false!

第二次指的是當處理bean下的成員變數要設為JSONObject的key map value時,當做到self的

setValue時會執行到

1005行 ==> jsonObject._setInternal( key, value );

2177行 ==> this.properties.put( key, _processValue( value ) );

在過來又回到fromObject,此時method又去判斷該value為int or JSONString or ....

再次執行_fromBean( object ); 此時再add當時set到self成員內的bean2,cycleSet發現Employee

類別下的object已存在了,因此會return false!因此程式便會進入

 return jsonConfig.getCycleDetectionStrategy()
               .handleRepeatedReferenceAsObject( bean );

JsonConfig.java預設會走Strict mode,因此jsonConfig.getCycleDetectionStrategy()物件所呼

叫的為StrictCycleDetectionStrategy的method而非LenientCycleDetectionStrategy
  1. public class JsonConfig {
  2.    private static final CycleDetectionStrategy DEFAULT_CYCLE_DETECTION_STRATEGY =
  3. CycleDetectionStrategy.STRICT;
  4.    private CycleDetectionStrategy cycleDetectionStrategy = DEFAULT_CYCLE_DETECTION_STRATEGY;
  5.    public CycleDetectionStrategy getCycleDetectionStrategy() {
  6.       return cycleDetectionStrategy;
  7.    }
  8.    .....
  9. }

CycleDetectionStrategy.java如下:
  1. private static final class LenientCycleDetectionStrategy extends CycleDetectionStrategy {
  2. public JSONArray handleRepeatedReferenceAsArray( Object reference ) {
  3.          return new JSONArray();
  4.       }
  5.       public JSONObject handleRepeatedReferenceAsObject( Object reference ) {
  6.          return new JSONObject( true );
  7.       }
  8.    }
  9. private static final class StrictCycleDetectionStrategy extends CycleDetectionStrategy {
  10.       public JSONArray handleRepeatedReferenceAsArray( Object reference ) {
  11.          throw new JSONException( "There is a cycle in the hierarchy!" );
  12.       }
  13.       public JSONObject handleRepeatedReferenceAsObject( Object reference ) {
  14.          throw new JSONException( "There is a cycle in the hierarchy!" );
  15.       }
  16.    }

若要防止Exception發生,可設定走
  1. JsonConfig jsonConfig = JsonConfig.getInstance();
  2. jsonConfig.setCycleDetectionStrategy(CycleDetectionStrategy.LENIENT);
  3. JSONObject object = JSONObject.fromObject(bean);

此時將會印出

{"employeeid":123,"department":"","employeename":"","self":null,"salary":0}

原本self的值應該為{"employeeid:....,此時會為null

或改code為bean2.setEmployeeid(456);

此時印出為

{"employeeid":123,"department":"","employeename":"","self":{"employeeid":456,"department":"","employeename":"","self":null,"salary":0},"salary":0}

留言