Java中如何从com.networknt.schema.JsonSchema获取指定层级属性及可选值
问题前提
给定JSON Schema
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://example.com/product.schema.json", "title": "Draft JSON Schema", "type": "object", "properties": { "person": { "type": "object", "properties": { "details": { "type": "object", "properties": { "first_name": { "type": "string" }, "last_name": { "type": "string" }, "groups": { "type": "array", "items": { "$ref": "#/$defs/existing_groups" } } } } }, "$defs": { "existing_groups": [ "Teachers", "Students" ] }, "book": { "type": "object", "properties": { "title": { "type": "string" }, "author": { "type": "string" } } } } }
需求描述
需要从上述schema中获取指定层级的可用属性及对应可选值,规则如下:
- 输入
person.details时,返回first_name、last_name、groups - 输入
person.details.groups时,返回其可选值Teachers、Students - 输入
book.title时,返回空数组或空集合
已知可通过JsonPath从JSON对象中提取属性值,现在需要从com.networknt.schema.JsonSchema实例中获取可选属性,有可选值定义时同时返回可选值,以下是Java最简单的实现方式。
实现方案
networknt的JsonSchema库没有直接提供层级属性遍历的工具,最简单的实现思路是直接操作Schema对应的JsonNode结构,逐层遍历路径并处理$ref引用即可。
第一步:引入依赖
Maven依赖示例:
<dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> <version>1.2.0</version> </dependency>
第二步:核心实现代码
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion; import java.util.*; public class SchemaPropertyExtractor { private static final ObjectMapper objectMapper = new ObjectMapper(); private static final JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012); public static List<String> extract(JsonSchema schema, String path) { JsonNode rootNode = schema.getSchemaNode(); String[] pathSegs = path.split("\\."); JsonNode currentNode = rootNode.get("properties"); // 逐层遍历路径 for (int i = 0; i < pathSegs.length; i++) { String seg = pathSegs[i]; currentNode = currentNode.get(seg); if (currentNode == null) return Collections.emptyList(); if (i != pathSegs.length - 1) { // 中间节点如果是数组类型,取items节点继续遍历 if (currentNode.has("type") && "array".equals(currentNode.get("type").asText())) { currentNode = resolveRef(currentNode.get("items"), rootNode); } currentNode = currentNode.get("properties"); if (currentNode == null) return Collections.emptyList(); } } // 处理最终节点 if (currentNode.has("type") && "object".equals(currentNode.get("type").asText())) { // 对象类型返回所有属性名 List<String> props = new ArrayList<>(); currentNode.get("properties").fieldNames().forEachRemaining(props::add); return props; } else if (currentNode.has("type") && "array".equals(currentNode.get("type").asText())) { // 数组类型解析items的引用,返回可选值 JsonNode itemsNode = resolveRef(currentNode.get("items"), rootNode); List<String> enums = new ArrayList<>(); itemsNode.forEach(item -> enums.add(item.asText())); return enums; } else { // 其他类型返回空集合 return Collections.emptyList(); } } // 处理$ref引用 private static JsonNode resolveRef(JsonNode node, JsonNode rootNode) { if (node.has("$ref")) { String refPath = node.get("$ref").asText().substring(2); // 去除前缀#/ String[] refSegs = refPath.split("/"); JsonNode refNode = rootNode; for (String seg : refSegs) { refNode = refNode.get(seg); } return refNode; } return node; } // 测试用例 public static void main(String[] args) throws Exception { // 加载Schema文件 JsonNode schemaNode = objectMapper.readTree(SchemaPropertyExtractor.class.getResourceAsStream("/product.schema.json")); JsonSchema schema = schemaFactory.getSchema(schemaNode); // 测试三个场景 System.out.println(extract(schema, "person.details")); // 输出 [first_name, last_name, groups] System.out.println(extract(schema, "person.details.groups")); // 输出 [Teachers, Students] System.out.println(extract(schema, "book.title")); // 输出 [] } }
说明
上述代码仅适配了示例中的简单场景,如果你有跨文件引用、嵌套引用、枚举类型等更复杂的Schema结构,扩展resolveRef方法和最终节点的判断逻辑即可。
内容的提问来源于stack exchange,提问作者AnjaM
相关产品推荐
相关产品推荐

