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

How to get all not exist keys after JsonConvert DeserializeObject in Json.Net?

您好,我正在使用 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 数据反序列化的过程中丢失了某些键,我会记录以查找为什么反序列化没有发生的问题 完全成功。

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

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

[JsonProperty(Required = Required.Always)] 

但这不会检查所有键。 这是否需要编写自定义 json 转换器?

使用此代码

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; }
    }

你的问题是双重的:

  1. 找到缺失的字段
  2. 找到额外的字段

在深入研究细节之前,让我们将 CharaData 分成两个 classes

[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 定义为 class 以便能够从中导出 << 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