使用json-schema校验json数据

1. 不想看2中具体代码实现的总结

  • json-schema本身也是json,但是可以用来描述另一个json应该符合的格式规范;
  • json-schema只是表示出规范,校验一个json是否符合这个json-schema的规范,需要代码实现;
  • 有很多开源的工具,可以用来校验,例如https://github.com/everit-org/json-schema和https://github.com/java-json-tools/json-schema-validator;
  • 本文的目的是将这个工具包装成一个注解,用来校验使用Spring的web工程中接口接收到的json参数。
2. 使用示例 【使用json-schema校验json数据】接口接收的json参数(这里只是举个例子):
{ "param1": 37.69437890136598, "param2": "enum1", "param3": true } 其中,param1是0~100的数字,必填,默认值为0;param2是枚举类型,有三个值enum1、enum2、enum3,默认值为enum1,必填;param3是布尔类型,默认值为true,非必填。

用来校验上面这个json的json-schema(我这里和前端交互,接口信息是写在yapi上,可以直接从yapi里面拷,是yapi根据接口描述自动生成的):
{ "type": "object", "title": "empty object", "properties": { "param1": { "type": "number", "minimum": 0, "maximum": 100, "default": "0" }, "param2": { "type": "string", "default": "enum1", "enum": [ "enum1", "enum2", "enum3" ], "enumDesc": "枚举类型,只有三个值enum1、enum2、enum3" }, "param3": { "type": "boolean", "default": true } }, "required": [ "param1", "param2" ] }

自定义注解:
import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.PARAMETER) @Constraint(validatedBy = JsonConstraintValidated.class) public @interface JsonValid {String value(); String message() default "Json数据校验失败"; Class[] groups() default {}; Class[] payload() default {}; }

实现注解具体功能的类:
import com.alibaba.fastjson.JSONObject; import com.chinatelecom.ctyun.xlive.common.exception.DataErrorException; import com.fasterxml.jackson.databind.JsonNode; import com.github.fge.jackson.JsonLoader; import com.github.fge.jsonschema.core.exceptions.ProcessingException; import com.github.fge.jsonschema.core.report.LogLevel; import com.github.fge.jsonschema.core.report.ProcessingReport; import com.github.fge.jsonschema.main.JsonSchema; import com.github.fge.jsonschema.main.JsonSchemaFactory; import org.springframework.util.StringUtils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.io.IOException; import java.util.Optional; public class JsonConstraintValidated implements ConstraintValidator {/** * 校验规则 */ private String jsonSchemaString; /** * 初始化方法,使用注解的地方,需要提供接口中定义好的参数,这里可以获得这些参数 */ @Override public void initialize(JsonValid constraintAnnotation) { jsonSchemaString = constraintAnnotation.value(); }/** * 校验方法 * * @param value待校验的json * @param context 将失败消息带出到Spring的参数校验异常类ConstraintViolationException * @return true:校验成功,无感知;false:校验失败,框架会抛出异常ConstraintViolationException */ @Override public boolean isValid(JSONObject value, ConstraintValidatorContext context) { try { String message = Optional.ofNullable(validJson(value, jsonSchemaString)).orElse(""); if (StringUtils.isEmpty(message)) { return true; } else { //禁用默认的message的值,重新添加错误提示语句 //默认的message无法体现具体是哪个json参数有问题 context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); return false; } } catch (Exception e) { throw new DataErrorException("json校验数据异常,请联系管理员"); } }/** * 使用开源工具https://github.com/java-json-tools/json-schema-validator校验json * * @param object被校验对象 * @param validParam 校验规则 * @return 返回失败消息,为空则校验成功 * @throws Exception */ public String validJson(JSONObject object, String validParam) throws IOException, ProcessingException { JsonNode schema = JsonLoader.fromString(validParam); JsonNode data = https://www.it610.com/article/JsonLoader.fromString(object.toJSONString()); final JsonSchema jsonSchema = JsonSchemaFactory.byDefault().getJsonSchema(schema); ProcessingReport report = jsonSchema.validate(data); StringBuilder sBuilder = new StringBuilder(); report.forEach(pm -> { if (LogLevel.ERROR.equals(pm.getLogLevel())) { JsonNode jsonNode = pm.asJson(); JsonNode key = Optional.ofNullable(jsonNode) .map(o -> o.get("instance")) .map(r -> r.get("pointer")) .orElse(null); sBuilder.append("errorKey:") .append(key == null ? "" : key.asText()) .append("; errorMessage:") .append(pm.getMessage()); } }); return sBuilder.toString(); } }

    推荐阅读