如何检测Draft 7版JSON Schema的循环依赖?求Java实现方案
检测JSON Schema Draft 7的循环依赖:现成库与实现方案
现成Java库方案
直接利用成熟的JSON Schema验证库的加载逻辑,这类库通常会在加载Schema时自动检测循环引用并抛出对应异常,你可以通过捕获异常判断是否存在循环依赖:
Everit JSON Schema:支持Draft 7,加载包含循环引用的Schema时会抛出
SchemaException,可通过异常信息判断是否为循环依赖导致import org.everit.json.schema.Schema; import org.everit.json.schema.loader.SchemaLoader; import org.json.JSONObject; public boolean hasCyclicDependency(JSONObject schemaJson) { try { SchemaLoader.load(schemaJson); return false; // 加载成功,无循环依赖 } catch (Exception e) { return e.getMessage().contains("circular reference"); } }networknt JSON Schema Validator:同样支持Draft 7,加载循环依赖的Schema时会抛出
JsonSchemaException,可通过异常信息判断。
自己实现的修正方案
你原来的伪代码逻辑存在缺陷:仅通过visited标记无法区分“已处理完成的节点”和“当前递归路径中的节点”,循环依赖的核心是在当前递归栈中再次遇到同一个节点。以下是修正后的实现思路:
核心逻辑
- 收集所有定义:先遍历Schema,把
definitions中的所有子Schema缓存起来,方便快速解析$ref内部引用。 - DFS+递归栈检测:用深度优先搜索遍历所有Schema节点,同时维护
visited(全局已处理节点)和recursionStack(当前递归路径节点),如果节点出现在recursionStack中,说明存在循环依赖。
示例代码
import org.json.JSONArray; import org.json.JSONObject; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class SchemaCycleChecker { public boolean hasCyclicDependency(JSONObject rootSchema) { Map<String, JSONObject> definitionMap = new HashMap<>(); collectDefinitions(rootSchema, definitionMap, "#"); Set<JSONObject> visited = new HashSet<>(); Set<JSONObject> recursionStack = new HashSet<>(); return dfs(rootSchema, definitionMap, visited, recursionStack); } private void collectDefinitions(JSONObject schema, Map<String, JSONObject> map, String currentPath) { // 收集definitions中的所有Schema if (schema.has("definitions")) { JSONObject definitions = schema.getJSONObject("definitions"); for (String key : definitions.keySet()) { String refPath = currentPath + "/definitions/" + key; JSONObject defSchema = definitions.getJSONObject(key); map.put(refPath, defSchema); // 递归收集子定义 collectDefinitions(defSchema, map, refPath); } } // 递归收集allOf中的子Schema的定义 if (schema.has("allOf")) { JSONArray allOf = schema.getJSONArray("allOf"); for (int i = 0; i < allOf.length(); i++) { JSONObject subSchema = allOf.getJSONObject(i); collectDefinitions(subSchema, map, currentPath + "/allOf/" + i); } } // 同理处理anyOf、oneOf等字段 if (schema.has("anyOf")) { JSONArray anyOf = schema.getJSONArray("anyOf"); for (int i = 0; i < anyOf.length(); i++) { JSONObject subSchema = anyOf.getJSONObject(i); collectDefinitions(subSchema, map, currentPath + "/anyOf/" + i); } } } private boolean dfs(JSONObject schema, Map<String, JSONObject> definitionMap, Set<JSONObject> visited, Set<JSONObject> recursionStack) { if (recursionStack.contains(schema)) { // 当前节点在递归栈中,存在循环依赖 return true; } if (visited.contains(schema)) { // 已处理完成,无循环 return false; } visited.add(schema); recursionStack.add(schema); // 处理$ref内部引用 if (schema.has("$ref")) { String refPath = schema.getString("$ref"); if (refPath.startsWith("#")) { JSONObject refSchema = definitionMap.get(refPath); if (refSchema != null && dfs(refSchema, definitionMap, visited, recursionStack)) { return true; } } // 外部引用可根据需求决定是否处理 } // 处理allOf子Schema if (schema.has("allOf")) { JSONArray allOf = schema.getJSONArray("allOf"); for (int i = 0; i < allOf.length(); i++) { JSONObject subSchema = allOf.getJSONObject(i); if (dfs(subSchema, definitionMap, visited, recursionStack)) { return true; } } } // 处理anyOf子Schema if (schema.has("anyOf")) { JSONArray anyOf = schema.getJSONArray("anyOf"); for (int i = 0; i < anyOf.length(); i++) { JSONObject subSchema = anyOf.getJSONObject(i); if (dfs(subSchema, definitionMap, visited, recursionStack)) { return true; } } } // 处理properties中的属性Schema if (schema.has("properties")) { JSONObject properties = schema.getJSONObject("properties"); for (String key : properties.keySet()) { JSONObject propSchema = properties.getJSONObject(key); if (dfs(propSchema, definitionMap, visited, recursionStack)) { return true; } } } // 处理数组items的Schema if (schema.has("items")) { Object items = schema.get("items"); if (items instanceof JSONObject) { if (dfs((JSONObject) items, definitionMap, visited, recursionStack)) { return true; } } else if (items instanceof JSONArray) { JSONArray itemsArray = (JSONArray) items; for (int i = 0; i < itemsArray.length(); i++) { JSONObject itemSchema = itemsArray.getJSONObject(i); if (dfs(itemSchema, definitionMap, visited, recursionStack)) { return true; } } } } recursionStack.remove(schema); return false; } }
关键说明
- 必须处理JSON Schema中所有可能包含子Schema的字段(
allOf/anyOf/oneOf/properties/items等),避免遗漏依赖关系。 $ref的内部引用需要通过definitions缓存快速定位目标Schema,外部引用可根据业务需求决定是否检测。
内容的提问来源于stack exchange,提问作者ColonelBobby
相关产品推荐
相关产品推荐

