在序列化之前根据模式验证对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在序列化之前根据模式验证对象相关的知识,希望对你有一定的参考价值。

我想将C#对象作为JSON序列化为流,但是如果对象根据模式无效则避免序列化。我应该如何使用JSON.NET和Json.NET Schema继续执行此任务?根据我的看法,JSON.NET库中没有允许根据JSON模式验证C#对象的方法。似乎有点奇怪的是,没有直接方法来验证C#对象而不对其进行编码。你知道为什么这种方法不可用吗?

答案

看来这个API目前还没有。猜测,这可能是因为递归生成要验证的JSON值涉及序列化对象的大部分工作。或者它可能只是因为没有人在Newtonsoft ever designed, specified, implemented, tested, documented and shipped that feature

如果你愿意,你可以file an enhancement request请求这个API,可能作为SchemaExtensions class的一部分。

与此同时,如果你确实需要测试验证POCO而不生成它的完整序列化(因为例如结果会非常大),你可以从NullJsonWriter获取Reference to automatically created objects,将其包装在JSchemaValidatingWriter中并测试序列化你的对象如Validate JSON with JSchemaValidatingWriter所示。 NullJsonWriter实际上并没有写任何内容,因此使用它可以消除生成完整序列化的性能和内存开销(无论是作为string还是作为JToken)。

首先,添加以下静态方法:

public static class JsonExtensions
{
    public static bool TestValidate<T>(T obj, JSchema schema, SchemaValidationEventHandler handler = null, JsonSerializerSettings settings = null)
    {
        using (var writer = new NullJsonWriter())
        using (var validatingWriter = new JSchemaValidatingWriter(writer) { Schema = schema })
        {
            int count = 0;
            if (handler != null)
                validatingWriter.ValidationEventHandler += handler;
            validatingWriter.ValidationEventHandler += (o, a) => count++;
            JsonSerializer.CreateDefault(settings).Serialize(validatingWriter, obj);
            return count == 0;
        }
    }
}

// Used to enable Json.NET to traverse an object hierarchy without actually writing any data.
class NullJsonWriter : JsonWriter
{
    public NullJsonWriter()
        : base()
    {
    }

    public override void Flush()
    {
        // Do nothing.
    }
}

然后使用它像:

// Example adapted from 
// https://www.newtonsoft.com/jsonschema/help/html/JsonValidatingWriterAndSerializer.htm
// by James Newton-King

string schemaJson = @"{
   'description': 'A person',
   'type': 'object',
   'properties': {
     'name': {'type':'string'},
     'hobbies': {
       'type': 'array',
       'maxItems': 3,
       'items': {'type':'string'}
     }
  }
}";         
var schema = JSchema.Parse(schemaJson);

var person = new
{
    Name = "James",
    Hobbies = new [] { ".Net", "Blogging", "Reading", "XBox", "LOLCATS" },
};

var settings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
var isValid = JsonExtensions.TestValidate(person, schema, (o, a) => Console.WriteLine(a.Message), settings);
// Prints Array item count 5 exceeds maximum count of 3. Path 'hobbies'.

Console.WriteLine("isValid = {0}", isValid); 
// Prints isValid = False

顺便注意案例。 Json.NET架构是case sensitive,因此在测试验证时需要使用适当的合同解析器。

样本fiddle

另一答案

你不能从JSON字符串中做到这一点,你需要一个对象和一个架构来与第一个进行比较。

public void Validate()
{
    //...
    JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}");
    JToken stringToken = JToken.FromObject("pie");
    stringToken.Validate(schema);

以上是关于在序列化之前根据模式验证对象的主要内容,如果未能解决你的问题,请参考以下文章

针对 Mongoose 模式验证对象而不保存为新文档

PHP面向对象之选择工厂和更新工厂

旋转时保留Fragment对象

保护代理模式-Access Proxy(Java实现)

策略模式-设计模式

单例模式-序列化问题