如何在 Json.Net 中的 JsonConvert DeserializeObject 之后获取所有不存在的键?

Posted

技术标签:

【中文标题】如何在 Json.Net 中的 JsonConvert DeserializeObject 之后获取所有不存在的键?【英文标题】:How to get all not exist keys after JsonConvert DeserializeObject in Json.Net? 【发布时间】:2021-12-29 14:03:03 【问题描述】:

您好,我正在使用 NewtonSoft Json.Net 反序列化我的 json 数据。我通常反序列化 json 字符串,但我想检查所有不存在的键。

例如这里是一个json数据。


    "Hp": 100,
    "PlayerInfo": 
        "Atk": 10,
        "Def": 20
    ,
    "Mp": 100
 

而且我有一个可以匹配上述数据的结构。

[Serializable]
public struct CharaData

    public int Hp;
    
    [Serializable]
    public struct PlayerInfoData
    
       public int Atk;
       public int Def;
       public int Spd; 
    
    PlayerInfoData PlayerInfo;

 

我会像这样对它进行去色化。

JsonConvert.DeserializeObject<CharaData>(jsonStr);

json 数据中有 Mp 键,但结构中没有。

PlayerInfoData 中,json 数据中没有Spd 键,但在结构中有一个Spd 字段。

嗯... Spd 字段似乎初始化为默认值 0,它可能是一个错误。

所以我想检查哪些键不在结构中。 以及哪些结构字段因为不存在而没有反序列化。

我会尽我所能防止这些发生,但是如果在从json数据反序列化的过程中缺少某些键,我会登录查找为什么没有反序列化的问题 完全成功。

[Error][CharaData::Mp key not exist in json string]
[Error][CharaData::PlayerInfo::Spd field not exist in struct]

似乎在 JsonConvert 类中没有任何方法可以检查它。 我看到了

[JsonProperty(Required = Required.Always)] 

但这不会检查所有的键。 这个需要写自定义的json转换器吗?

【问题讨论】:

【参考方案1】:

使用此代码

var result= JsonConvert.DeserializeObject<CharaData>(jsonStr);

var mp=result.Mp;
var playerInfo=result.PlayerInfo;

如果您想知道存在哪些键,只需检查它们是否为空。默认情况下,所有键都是空的。如果它们不为空,则表示它们从 json 中获取值。例如,您可以使用此代码

if (mp==null) Console.WriteLine ("mp is not exist in json");

另一种方法是使用反射检查所有属性

    var props = result.GetType().GetProperties();
    var nulls = new List<string>();

    foreach (var prop in props)
    
        var propInstance = prop.GetValue(result, null);

        if (propInstance == null) nulls.Add(prop.Name);

        if (prop.Name == "PlayerInfo")
        
            var prps = prop.PropertyType.GetProperties();
            foreach (var prp in prps)
                if (prp.GetValue(propInstance, null) == null) nulls.Add(prop.Name+"."+prp.Name);
        

    
    foreach (var n in nulls)
        Console.WriteLine(n + " doesn't have value");

测试结果

PlayerInfo.Spd doesn't have value

public class PlayerInfo
    
        public int? Atk  get; set; 
        public int? Def  get; set; 
        public int? Spd  get; set;  
    

    public class CharaData
    
        public int? Hp  get; set; 
        public PlayerInfo PlayerInfo  get; set; 
        public int? Mp  get; set; 
    

【讨论】:

【参考方案2】:

你的问题是双重的:

    查找缺失的字段 查找额外字段

在深入了解细节之前,让我们将CharaData 分为两个类

[Serializable]
public class CharaData

    public int Hp;
    public PlayerInfoData PlayerInfo;


[Serializable]
public class PlayerInfoData

    public int Atk;
    public int Def;
    public int Spd;

缺少字段

此解决方案依赖于JsonSchema

private static Lazy<JSchema> schema = new Lazy<JSchema>(() => 
    var generator = new JSchemaGenerator();
    return generator.Generate(typeof(CharaData));
, true);

public static void ReportMissingFields(string json)

    var semiParsed = JObject.Parse(json);
            
    try
    
        semiParsed.Validate(schema.Value);
    
    catch (JSchemaValidationException ex)
    
        Console.WriteLine(ex.ValidationError.Message);
    

schema 以惰性方式存储 CharaData 的 json 架构 Validatejson 与架构进行比较,如果不匹配,则抛出 JSchemaValidationException 它公开了一个类型为ValidationError 的属性,其中包含大量有关不匹配的信息

额外字段

此解决方案依赖于JsonExtensionDataAttribute

[Serializable]
internal class CharaDataExtras: CharaData

    [JsonExtensionData]
    public IDictionary<string, JToken> ExtraFields;


...

public static void ReportExtraFields(string json)

    var result = JsonConvert.DeserializeObject<CharaDataExtras>(json);
    foreach (var field in result.ExtraFields)
    
        Console.WriteLine($"An extra field has found, called field.Key");
    

我已将CharaData 定义为能够从中派生的类CharaDataExtras 每个额外的字段都将被放入ExtraFields 字典中

用法

var json = File.ReadAllText("sample.json");
ReportMissingFields(json);
ReportExtraFields(json);

输出:

Required properties are missing from object: Spd.
An extra field has found, called Mp

【讨论】:

以上是关于如何在 Json.Net 中的 JsonConvert DeserializeObject 之后获取所有不存在的键?的主要内容,如果未能解决你的问题,请参考以下文章

如何在JSON.NET中读取json对象值中的long值数组

如何在 .net core 3.1 中的 Newtonsoft JsonConverter 中注入依赖项

如何使用与 NewtonSoft (JSON.Net) 组件中的 JSON 匹配的 Swift 类从/向 JSON 读取/写入对象数组?

如何在JSON.NET中unescape unicode

.net 3.5 中的反序列化对象

使用 JSON.NET 反序列化 DateTime 时如何保留时区? [复制]