Json.NET Schema:JToken.IsValid()上下文传递及JSchema线程安全问询
解决方案
1. 为每次验证传递实例专属上下文
Json.NET Schema 提供了支持传递自定义上下文的验证重载方法,可解决实例专属信息无法绑定到复用JSchema/JsonValidator的问题:
- 首先定义存储实例专属信息的上下文类:
public class ValidationGroupContext { public Dictionary<string, Type> TypeMappings { get; set; } // 存储组对应的name-value类型映射规则 }
- 在自定义
ValueTypeValidator中,通过ValidationContext.UserState获取上下文:
public class ValueTypeValidator : JsonValidator { public override void Validate(JToken value, JsonValidatorContext validationContext) { // 获取当前验证传递的上下文 var groupContext = validationContext.UserState as ValidationGroupContext; if (groupContext == null) return; // 提取当前节点的name和value字段 var nameToken = value.SelectToken("name"); var valueToken = value.SelectToken("value"); if (nameToken == null || valueToken == null) return; string fieldName = nameToken.ToString(); if (groupContext.TypeMappings.TryGetValue(fieldName, out Type expectedType)) { // 验证value类型是否匹配组规则 JTokenType expectedTokenType = MapClrTypeToJTokenType(expectedType); if (valueToken.Type != expectedTokenType) { validationContext.ReportError($"字段'{fieldName}'的值类型应为{expectedType.Name}", valueToken.Path); } } } // 辅助方法:CLR类型与JTokenType的映射逻辑 private JTokenType MapClrTypeToJTokenType(Type type) { if (type == typeof(string)) return JTokenType.String; if (type == typeof(int) || type == typeof(long)) return JTokenType.Integer; if (type == typeof(bool)) return JTokenType.Boolean; if (type == typeof(double)) return JTokenType.Float; // 按需补充其他类型映射 return JTokenType.Object; } }
- 调用验证时,通过重载方法传入上下文:
// 构建当前实例专属的组上下文 var groupContext = new ValidationGroupContext { TypeMappings = new Dictionary<string, Type> { { "age", typeof(int) }, { "username", typeof(string) }, { "isActive", typeof(bool) } } }; // 待验证的JSON实例 JToken targetJson = JObject.Parse("{\"name\":\"age\",\"value\":\"25\"}"); // 复用的JSchema(已预先配置ValueTypeValidator) JSchema reusableSchema = JSchema.Parse(@"{ ""type"": ""object"", ""properties"": { ""name"": { ""type"": ""string"" }, ""value"": {} }, ""validators"": [{ ""$type"": ""YourNamespace.ValueTypeValidator, YourAssembly"" }] }"); // 传递上下文执行验证 if (!targetJson.IsValid(reusableSchema, out IList<ValidationError> errors, groupContext)) { foreach (var error in errors) { Console.WriteLine($"验证错误:{error.Message}"); } }
关于之前尝试的方法说明
JSchemaValidatingReader:该阅读器的上下文无法直接传入JsonValidator.Validate方法,而通过ValidationContext.UserState是更直接的上下文传递路径。- 绑定
JToken的方法:Root属性指向整个JSON树的根节点,若验证节点不是根节点或JToken被克隆,会导致Root指向错误;通过UserState传递上下文完全规避了这个问题。
2. JSchema的线程安全性与复用性
JSchema对象是**不可变(Immutable)**的,一旦创建就不会修改内部状态,因此它天然是线程安全、可重入的——可以安全地用于多个不同JSON实例的并发验证。这也是官方建议复用JSchema的核心原因:避免重复解析Schema带来的性能开销。
内容的提问来源于stack exchange,提问作者yury
相关产品推荐
相关产品推荐

