You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 22:29:55