FastJson $href 循环引用解决方案

FastJson $href循环引用解决方案

  • 问题背景
由于项目中需要对微服务中Swagger-ui中生成的API json 内容解析,所以准备采用FastJson 
组件进行解析Json内容,在解析的过程中出现$ref循环引用和重复引用问题。
  • 需要解析的 Json 内容
{
	"swagger":"2.0",
	"info":{"description":"API","version":"2.0","title":"Api"},
	"host":"10.10.4.66:19021",
	"basePath":"/",
	"tags":[
		{"name":"demo-robot-api","description":"Demo Robot Api"},
		{"name":"echo-api","description":"Echo Api"},
		{"name":"hystrix-api","description":"Hystrix Api"},
		{"name":"value-api","description":"Value Api"}
	],
	"paths":{
		"/demo/echo/echoObjectByObject":{
			"post":{
				"tags":["echo-api"],
				"summary":"传入对象,返回对象",
				"operationId":"echoObjectByObjectUsingPOST",
				"consumes":["application/json"],
				"produces":["*/*"],
				"parameters":[
					{
						"name":"time",
						"in":"query",
						"required":false,
						"type":"string",
						"format":"date-time"
					},
					{
						"name":"value",
						"in":"query",
						"required":false,
						"type":"string"
					}
				],
				"responses":{
					"200":{
						"description":"OK",
						"schema":{"$ref":"#/definitions/ApiResponse«EchoVo»"}
					}
				},
				"deprecated":false
			}
		},
			"post":{
				"tags":["demo-robot-api"],
				"summary":"更新子对象",
				"operationId":"updateJobUsingPOST",
				"consumes":["application/json"],
				"produces":["*/*"],
				"parameters":[
					{
						"in":"body",
						"name":"vo",
						"description":"vo",
						"required":true,
						"schema":{"$ref":"#/definitions/DemoRobotJobVo"}
					}
				],
				"responses":{
					"200":{
						"description":"OK",
						"schema":{"$ref":"#/definitions/ApiResponse«string»"}
					}
				},
				"deprecated":false
			}
		},
		"/robot/demo/updateMain":{
			"post":{
				"tags":["demo-robot-api"],
				"summary":"更新主对象",
				"operationId":"updateMainUsingPOST",
				"consumes":["application/json"],
				"produces":["*/*"],
				"parameters":[
					{
						"in":"body",
						"name":"vo",
						"description":"vo",
						"required":true,
						"schema":{"$ref":"#/definitions/DemoRobotMainVo"}
					}
				],
				"responses":{
					"200":{
						"description":"OK",
						"schema":{"$ref":"#/definitions/ApiResponse«string»"}
					}
				},
				"deprecated":false
			}
		}
	},
	"definitions":{
		"ApiResponse«DemoRobotJobVo»":{
			"type":"object",
			"properties":{
				"data":{"type":"object"},
				"status":{"type":"integer","format":"int64"},
				"statusText":{"type":"string"}
			},
			"title":"ApiResponse«DemoRobotJobVo»"
		},
		"ApiResponse«DemoRobotMainVo»":{
			"type":"object",
			"properties":{
				"data":{"type":"object"},
				"status":{"type":"integer","format":"int64"},
				"statusText":{"type":"string"}
			},
			"title":"ApiResponse«DemoRobotMainVo»"
		},
		"ApiResponse«EchoVo»":{
			"type":"object",
			"properties":{
				"data":{"type":"object"},
				"status":{"type":"integer","format":"int64"},
				"statusText":{"type":"string"}
			},
			"title":"ApiResponse«EchoVo»"
		},
		"ApiResponse«ResultPage«DemoRobotJobVo»»":{
			"type":"object",
			"properties":{
				"data":{"type":"object"},
				"status":{"type":"integer","format":"int64"},
				"statusText":{"type":"string"}
			},
			"title":"ApiResponse«ResultPage«DemoRobotJobVo»»"
		},
		"ApiResponse«ResultPage«DemoRobotMainVo»»":{
			"type":"object",
			"properties":{
				"data":{"type":"object"},
				"status":{"type":"integer","format":"int64"},
				"statusText":{"type":"string"}
			},
			"title":"ApiResponse«ResultPage«DemoRobotMainVo»»"
		},
		"ApiResponse«ResultPage«EchoVo»»":{
			"type":"object",
			"properties":{
				"data":{"type":"object"},
				"status":{"type":"integer","format":"int64"},
				"statusText":{"type":"string"}
			},
			"title":"ApiResponse«ResultPage«EchoVo»»"
		},
		"ApiResponse«string»":{
			"type":"object",
			"properties":{
				"data":{"type":"object"},
				"status":{"type":"integer","format":"int64"},
				"statusText":{"type":"string"}
			},
			"title":"ApiResponse«string»"
		},
		"DemoRobotJobVo":{
			"type":"object",
			"properties":{
				"comCode":{"type":"string"},
				"consumeEnergy":{"type":"number"},
				"endTime":{"type":"string","format":"date-time"},
				"id":{"type":"integer","format":"int64"},
				"insertTimeForHis":{"type":"string","format":"date-time"},
				"jobContent":{"type":"string"},
				"jobImage":{"type":"string","format":"byte"},
				"operateTimeForHis":{"type":"string","format":"date-time"},
				"robotId":{"type":"integer","format":"int64"},
				"startTime":{"type":"string","format":"date-time"},
				"version":{"type":"integer","format":"int32"},
				"walkCount":{"type":"integer","format":"int64"}
			},
			"title":"DemoRobotJobVo"
		},
		"DemoRobotMainVo":{
			"type":"object",
			"properties":{
				"comCode":{"type":"string"},
				"id":{"type":"integer","format":"int64"},
				"insertTimeForHis":{"type":"string","format":"date-time"},
				"jobList":{
					"type":"array",
					"items":{"$ref":"#/definitions/DemoRobotJobVo"}
				},
				"manufactureDate":{"type":"string","format":"date-time"},
				"manufactureName":{"type":"string"},
				"nickname":{"type":"string"},
				"operateTimeForHis":{"type":"string","format":"date-time"},
				"rechargeCount":{"type":"integer","format":"int32"},
				"robotHeight":{"type":"number"},
				"robotSn":{"type":"string"},
				"version":{"type":"integer","format":"int32"}
			},
			"title":"DemoRobotMainVo"
		},
		"EchoVo":{
			"type":"object",
			"properties":{
				"time":{"type":"string","format":"date-time"},
				"value":{"type":"string"}
			},
			"title":"EchoVo"
		},
		"ResultPage«DemoRobotJobVo»":{
			"type":"object",
			"properties":{
				"data":{"type":"array","items":{"$ref":"#/definitions/DemoRobotJobVo"}},
				"pageNo":{"type":"integer","format":"int64"},
				"perPage":{"type":"integer","format":"int64"},
				"totalCount":{"type":"integer","format":"int64"}
			},
			"title":"ResultPage«DemoRobotJobVo»"
		},
		"ResultPage«DemoRobotMainVo»":{
			"type":"object",
			"properties":{
				"data":{
					"type":"array",
					"items":{"$ref":"#/definitions/DemoRobotMainVo"}
				},
				"pageNo":{
				"type":"integer","format":"int64"},
				"perPage":{"type":"integer","format":"int64"},
				"totalCount":{"type":"integer","format":"int64"}
			},
			"title":"ResultPage«DemoRobotMainVo»"
		},
		"ResultPage«EchoVo»":{
			"type":"object",
			"properties":{
				"data":{
					"type":"array",
					"items":{"$ref":"#/definitions/EchoVo"}
				},
				"pageNo":{
					"type":"integer",
					"format":"int64"
				},
				"perPage":{
					"type":"integer",
					"format":"int64"
				},
				"totalCount":{
					"type":"integer",
					"format":"int64"
				}
			},
			"title":"ResultPage«EchoVo»"
		}
	}
}
  • 阿里Json解析组件 FastJson,
	<dependency>
	    <groupId>com.alibaba</groupId>
	    <artifactId>fastjson</artifactId>
	    <version>1.2.58</version>
	</dependency>
  • Json字符串解析方法
public void parseJson(String json) {
    JSONObject jsonObject = JSONObject.parseObject(json);
    JSONObject apiPaths = (JSONObject)jsonObject.get("paths");
    Set<String> keySet = apiPaths.keySet();
    for(String key : keySet) {
        JSONObject apiPath = (JSONObject)apiPaths.get(key);
        Set<String> methodType = apiPath.keySet();
        for(String type : methodType) {
            System.out.println("type: "+type);
            JSONObject jsonApi = (JSONObject) apiPath.get(type);
            API api = JSONObject.parseObject(jsonApi.toJSONString(), API.class);
            System.out.println(api);
        }
    }
}

API json对象vo类
public class API {
	
	public String summary;
	
	public String deprecated;
	
	public String produces;
	
	public String operationId;
	
	public String tags;
	
	public JSONArray  parameters;
	
	public JSON responses;
	
	public String consumes;

   //setter/getter 省略
}
  • 解析结果
type: get
API [summary=插入Test, deprecated=false, produces=["*/*"], 
operationId=insertTestDataUsingGET, tags=["demo-robot-api"], 
parameters=[{"in":"path","name":"jobCount","format":"int32",
"description":"jobCount","type":"integer","required":true},
{"in":"path","name":"mainCount","format":"int32",
"description":"mainCount","type":"integer","required":true}],
responses={"200":{"schema":{"$ref":"@"},"description":"OK"}}, consumes=null]
type: post
API [summary=根据生产厂家查找对象(包含可能的子对象),
 deprecated=false, produces=["*/*"], 
 operationId=selectByManufactureNameUsingPOST, 
tags=["demo-robot-api"], 
parameters[{"in":"query","name":"manufactureName",
"description":"manufactureName","type":"string","required":true}], 
responses={"200":{"schema":{"$ref":"@"},"description":"OK"}}, 
consumes=["application/json"]]
...
....
  • 问题描述
上述中对于response中的对象引用全部被解析为 “@”
  • 问题原因
解析为@是由于对象存在着循环引用或重复引用,即同一个对象在多个地方都
有引用,所以导致解析出错
  • 解决方案
1、可以通过关闭全局的默认循环引用检测
JSON.DEFAULT_PARSER_FEATURE = Feature.DisableCircularReferenceDetect.getMask();

2、可以在需要解析的地方,指定关闭循环引用检测
JSONObject jsonObject = JSONObject.parseObject(json,
		Feature.DisableCircularReferenceDetect);
  • FastJson 关闭循环引用 注意
1、需要注意FastJson的版本,该方案是基于1.2.58,在版本1.2.4中则会
解析失败,出现空指针异常,建议使用高版本

2、在关闭循环引用检测的时候,需要注意采用的为 Feature 的枚举类型,而
不是SerializerFeature中的枚举类型
  • 最终解析代码为
public void parseJson(String json) {
    JSON.DEFAULT_PARSER_FEATURE = Feature.DisableCircularReferenceDetect.getMask();
    JSONObject jsonObject = JSONObject.parseObject(json);
    JSONObject apiPaths = (JSONObject)jsonObject.get("paths");
    Set<String> keySet = apiPaths.keySet();
    for(String key : keySet) {
        JSONObject apiPath = (JSONObject)apiPaths.get(key);
        Set<String> methodType = apiPath.keySet();
        for(String type : methodType) {
            System.out.println("type: "+type);
            JSONObject jsonApi = (JSONObject) apiPath.get(type);
            API api = JSONObject.parseObject(jsonApi.toJSONString(), API.class);
            System.out.println(api);
        }
    }
}
  • 解析的结果为
type: get
API [summary=home, deprecated=false, produces=["*/*"], 
operationId=homeUsingGET, tags=["hystrix-api"], 
parameters=[{"in":"query","name":"name","description":"name",
"type":"string","required":false}], 
responses={"200":{"schema":{"$ref":"#/definitions/ApiResponse«string»"},
"description":"OK"}}, consumes=null]

由此可知,上述结果中response的 $href 为对应的引用对象

猜你喜欢

转载自blog.csdn.net/yaoqiancuo3276/article/details/91817807