简体   繁体   English

使用 JSON 模式,如何在将 JSON 解析为 JObject 时过滤掉其他属性?

[英]Using a JSON Schema, how can I filter out additional properties when parsing JSON to a JObject?

I am trying to parse only part of provided JSON.我正在尝试仅解析提供的 JSON 的一部分。 I am using Newtonsoft.Json.Schema nuget.我正在使用 Newtonsoft.Json.Schema nuget。 For the next example, I want to deserialize only name and age properties.对于下一个示例,我只想反序列化 name 和 age 属性。

JSchema schema = JSchema.Parse(@"{
   'id': 'person',
   'type': 'object',
   'additionalProperties' : false,
   'properties': {
   'name': {'type':'string'},
   'age': {'type':'integer'}
   }
}");
            

JsonTextReader reader = new JsonTextReader(new StringReader(@"{
    'name': 'James',
    'age': 29,
    'salary': 9000.01,
    'jobTitle': 'Junior Vice President'
}"));

JSchemaValidatingReader validatingReader = new JSchemaValidatingReader(reader);
validatingReader.Schema = schema;

JsonSerializer serializer = new JsonSerializer();
JObject data = serializer.Deserialize<JObject>(validatingReader);

If I will set 'additionalProperties': true I will get unnecessary fields deserialized.如果我将设置'additionalProperties': true我将反序列化不必要的字段。

在此处输入图像描述

But if I will set 'additionalProperties': false , I will receive an error:但是如果我设置'additionalProperties': false ,我会收到一个错误:

Newtonsoft.Json.Schema.JSchemaValidationException: Property 'salary' has not been defined and the schema does not allow additional properties. Newtonsoft.Json.Schema.JSchemaValidationException:尚未定义属性“薪水”,并且架构不允许其他属性。 Path 'salary', line 4, position 11.路径“工资”,第 4 行,position 11。

Note that I will know the needed fields only in runtime.请注意,我只会在运行时知道所需的字段。 I receive big JSON and I need to create some solution to deserialize only part of this JSON.我收到了大的 JSON,我需要创建一些解决方案来反序列化这个 JSON 的一部分。 And users should decide which properties should be processed and which aren't.用户应该决定哪些属性应该被处理,哪些不是。

JSchemaValidatingReader Represents a reader that provides JSchema validation . JSchemaValidatingReader表示提供 JSchema验证的阅读器。 It does not provide any sort of filtering capability.它不提供任何类型的过滤功能。

What you could do instead is to load your JSON into a JToken , validate with SchemaExtensions.IsValid(JToken, JSchema, out IList<ValidationError>) , and then remove additional properties at the path indicated by ValidationError.Path .您可以做的是将 JSON 加载到JToken中,使用SchemaExtensions.IsValid(JToken, JSchema, out IList<ValidationError>) ,然后在ValidationError.Path指示的路径中删除其他属性。

To do this, modify your code as follows:为此,请按如下方式修改您的代码:

var data = JObject.Parse(jsonString); // The string literal from your question

var isValid = data.IsValid(schema, out IList<ValidationError> errors);

if (!isValid)
{           
    foreach (var error in errors)
    {
        if (error.ErrorType == ErrorType.AdditionalProperties)
            data.SelectToken(error.Path)?.RemoveFromLowestPossibleParent();
    }
}

using the extension method:使用扩展方法:

public static partial class JsonExtensions
{
    public static JToken RemoveFromLowestPossibleParent(this JToken node)
    {
        if (node == null)
            return null;
        // If the parent is a JProperty, remove that instead of the token itself.
        var property = node.Parent as JProperty;
        var contained = property ?? node;
        if (contained.Parent != null)
            contained.Remove();
        // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
        if (property != null)
            property.Value = null;
        return node;
    }
}

Notes:笔记:

  • When the error type is ErrorType.AdditionalProperties the Path will point directly to the unwanted property, but for other error types such as ErrorType.Required the path may point to the parent container .当错误类型为ErrorType.AdditionalProperties时, Path将直接指向不需要的属性,但对于其他错误类型,例如ErrorType.Required ,路径可能指向父容器 Thus you should check the error type before removing a token related to an error at a given path.因此,您应该在删除与给定路径中的错误相关的令牌之前检查错误类型。

  • If your JSON is large, it is recommended to deserialize directly from a stream using JsonSerializer.CreateDefault().Deserialize<JToken>(reader) (as you are doing currently) to avoid loading the JSON into an intermediate string. If your JSON is large, it is recommended to deserialize directly from a stream using JsonSerializer.CreateDefault().Deserialize<JToken>(reader) (as you are doing currently) to avoid loading the JSON into an intermediate string.

Demo fiddle here .演示小提琴在这里

I don't think there is any built-in method for your usecase, because the additionalProperties is meant to either forbid/allow additional properties.我认为您的用例没有任何内置方法,因为additionalProperties旨在禁止/允许其他属性。 But once they are allowed in the schema, they are also deserialized.但是一旦它们在模式中被允许,它们也会被反序列化。 It doesn't make much sense to me to allow additional properties in the schema, but then don't allow them to show up in the data.允许架构中的其他属性对我来说没有多大意义,但不允许它们出现在数据中。 Maybe you can explain your usecase?也许你可以解释你的用例?

The simplest would probably be to deserialize to a class instead of JObject .最简单的可能是反序列化为 class 而不是JObject And in that class only define the properties you would like to see并且在那 class 只定义你想看到的属性

class Person {
  [JsonProperty("name")];
  public string Name {get;set;}

  [JsonProperty("age")];
  public int Age {get;set;}
}

...

Person p = serializer.Deserialize<Person>(validatingReader);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM