【问题标题】:Validate object against a schema before serialization在序列化之前根据模式验证对象
【发布时间】:2017-12-22 21:12:57
【问题描述】:

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

【问题讨论】:

  • 您可以从Reference to automatically created objects 中获取NullJsonWriter,然后将其包装在JSchemaValidatingWriter 中并测试序列化您的对象,如Validate JSON with JSchemaValidatingWriter 所示。
  • 似乎有些奇怪,没有直接的方法来验证 C# 对象而不对其进行编码。 -- 可能是因为生成要验证的值是 90% 的工作编码吗? (诸如“为什么 X 公司做 Y”之类的问题对于 stackoverflow 来说大多是 OT 的。)
  • 嗯,这是一个Vexing Exception,所以你只需要序列化它然后测试它。你无法避免对其进行序列化。
  • @bosonix - 再次只是在这里猜测,但要“验证对象”,Json.NET 需要降低对象层次结构,为遇到的每个对象生成契约,从成员中获取值,转换原语将值转换为调用所有相关转换器的 JSON 格式,然后将序列化的原始值与 JSON 模式中的原始值进行比较。这似乎超过了序列化工作的 50%。请注意,json-schema.org 用于注释和验证 JSON 文档。 因此,要验证 对象,您需要使对象看起来像 JSON。

标签: c# .net json json.net jsonschema


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

    你不能从 JSON 字符串中做到这一点,你需要一个对象和一个模式来与第一个比较..

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

    【讨论】:

      猜你喜欢
      • 2018-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-08
      • 1970-01-01
      • 2013-07-21
      相关资源
      最近更新 更多