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

C# 使用JSON模式,在将JSON解析为JObject时,如何过滤掉其他属性?,c#,json,json.net,jsonschema,json-deserialization,C#,Json,Json.net,Jsonschema,Json Deserialization,我试图只解析提供的JSON的一部分。我正在使用Newtonsoft.Json.Schema nuget。 对于下一个示例,我只想反序列化name和age属性 JSchema schema = JSchema.Parse(@"{ 'id': 'person', 'type': 'object', 'additionalProperties' : false, 'properties': { 'name': {'type':'string'}, 'age

我试图只解析提供的JSON的一部分。我正在使用Newtonsoft.Json.Schema nuget。 对于下一个示例,我只想反序列化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);
JSchema schema=JSchema.Parse(@){
“id”:“person”,
“类型”:“对象”,
“additionalProperties”:false,
“属性”:{
'name':{'type':'string'},
'age':{'type':'integer'}
}
}");
JsonTextReader reader=新JsonTextReader(新StringReader(@){
'姓名':'詹姆斯',
年龄:29岁,,
“工资”:9000.01,
“职务头衔”:“初级副总裁”
}"));
JSChemAvalidangReader validatingReader=新的JSChemAvalidangReader(读取器);
validatingReader.Schema=Schema;
JsonSerializer serializer=新的JsonSerializer();
JObject data=serializer.Deserialize(validatingReader);
如果我将设置
“additionalProperties”:true
我将反序列化不必要的字段

但是如果我将设置
“additionalProperties”:false,我将收到一个错误:

Newtonsoft.Json.Schema.JSchemaValidationException:尚未定义属性“salary”,并且架构不允许其他属性。路径“工资”,第4行,职位11。


请注意,我将只在运行时知道所需的字段。我收到了大的JSON,我需要创建一些解决方案来反序列化这个JSON的一部分。用户应该决定哪些属性应该被处理,哪些属性不应该被处理。

我认为没有任何内置的方法用于您的用例,因为
additionalProperties
意味着禁止/允许附加属性。但一旦模式中允许它们,它们也会被反序列化。在模式中允许附加属性对我来说没有多大意义,但是不允许它们显示在数据中。也许你可以解释一下你的用例

最简单的可能是反序列化到类,而不是
JObject
。在该类中,只定义您希望看到的属性

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

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

...

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

班级人员{
[JsonProperty(“名称”)];
公共字符串名称{get;set;}
[JsonProperty(“年龄”)];
公共整数{get;set;}
}
...
Person p=序列化程序。反序列化(validatingReader);
表示提供JSchema验证的读取器。它不提供任何类型的过滤功能

相反,您可以将JSON加载到
JToken
中,使用进行验证,然后在指定的路径上删除其他属性

为此,请按如下方式修改代码:

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();
    }
}
注:

  • 当错误类型为
    ErrorType.AdditionalProperties
    时,
    路径将直接指向不需要的属性,但对于其他错误类型,如路径,则可能指向父容器。因此,在移除与给定路径上的错误相关的令牌之前,应该检查错误类型

  • 如果您的JSON很大,建议您使用
    JsonSerializer.CreateDefault()。直接从流中反序列化。反序列化(读取器)
    (正如您当前所做的),以避免将JSON加载到中间字符串中


演示小提琴。

不确定这是否有效,但尝试使用
'allowAdditionalProperties':true
而不是
'additionalProperties':true
@Thomas如果我使用
'allowAdditionalProperties':false
,它仍然会解析工资和职务属性。但我不想在结果中看到它们这是我的主要问题,我只会在运行时知道所需的字段。我收到了大的JSON,我需要创建一些解决方案来反序列化这个JSON的一部分。用户应该决定哪些属性应该处理,哪些不应该。也许您有一些其他的解决方案,而不是使用JSchema?当然,您可以在运行时创建具有所需属性的类型,如此处所示,或者您可以在反序列化后对
数据
对象进行后处理,并删除所有不需要的属性。但我不认为有任何可能只是“部分”反序列化json字符串。乍一看,它是有效的。我将尝试更多的案例,然后将您的评论标记为答案。非常感谢你!
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;
    }
}