问题描述
我试图仅解析提供的 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);
如果我设置 'additionalProperties' : true
,我会反序列化不必要的字段。
但是如果我设置 'additionalProperties' : false
,我会收到一个错误:
Newtonsoft.Json.Schema.JSchemaValidationException:尚未定义属性“薪水”,并且架构不允许附加属性。路径 'salary',第 4 行,位置 11。
请注意,我只会在运行时知道所需的字段。我收到了很大的 JSON,我需要创建一些解决方案来反序列化这个 JSON 的一部分。并且用户应该决定哪些属性应该被处理,哪些不应该被处理。
解决方法
JSchemaValidatingReader
代表提供 JSchema 验证的阅读器。它不提供任何类型的过滤功能。
您可以做的是将您的 JSON 加载到 JToken
中,使用 SchemaExtensions.IsValid(JToken,JSchema,out IList<ValidationError>)
进行验证,然后在 ValidationError.Path
指示的路径中删除其他属性。
为此,请按如下方式修改您的代码:
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();
}
}
使用扩展方法:
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;
}
}
注意事项:
-
当错误类型为
ErrorType.AdditionalProperties
时,Path
将直接指向不需要的属性,但对于其他错误类型,例如ErrorType.Required
,路径可能指向 父容器。因此,您应该在删除与给定路径上的错误相关的标记之前检查错误类型。 -
如果您的 JSON 很大,建议使用
JsonSerializer.CreateDefault().Deserialize<JToken>(reader)
直接从流中反序列化(正如您目前所做的那样),以避免将 JSON 加载到中间字符串中。
演示小提琴here。
,我认为您的用例没有任何内置方法,因为 additionalProperties
旨在禁止/允许其他属性。但是一旦它们在模式中被允许,它们也会被反序列化。在模式中允许其他属性对我来说没有多大意义,但不允许它们显示在数据中。也许您可以解释一下您的用例?
最简单的方法可能是反序列化为一个类而不是 JObject
。在那个类中只定义你想看到的属性
class Person {
[JsonProperty("name")];
public string Name {get;set;}
[JsonProperty("age")];
public int Age {get;set;}
}
...
Person p = serializer.Deserialize<Person>(validatingReader);