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

Time:11-20

Hello I'm using NewtonSoft Json.Net to deserialize my json data. I usually deserialize json string but I want to check all of not exist keys.

For example here is a json data.

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

And I have a structure which can match above data.

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

And I'm gonna deseialize it like this.

JsonConvert.DeserializeObject<CharaData>(jsonStr);
  • There is a Mp key in json data but in structure there is not.

  • And in PlayerInfoData there is no Spd key in json data but in structure there is a Spd field.

    • Well... Spd field seems initialize as a default 0 value and it could potentially be a bug.

So I want to check what keys are not in structure. And what structure fields are not deserialized because of not exist.

I will do my best to prevent these happening, but if some keys are missing in the process of deserializing from json data, I will log to find the problem why deserialize wasn't completely success.

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

Seems there is no any method to check it in JsonConvert class. I saw

[JsonProperty(Required = Required.Always)] 

but this does not check all of keys. Is this need to write a custom json converter?

CodePudding user response:

use this code

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

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

if you want to know what key are exist just check them for null. By default all keys are null. if they are not null, it means they took value from json. For example you can use this code

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

another way is to use reflection to check all properties

    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");

test result

PlayerInfo.Spd doesn't have value

classes

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

CodePudding user response:

Your problem is twofold:

  1. Find the missing fields
  2. Find the extra fields

Before we are digging into the details let's split the CharaData into two classes

[Serializable]
public class CharaData
{
    public int Hp;
    public PlayerInfoData PlayerInfo;
}

[Serializable]
public class PlayerInfoData
{
    public int Atk;
    public int Def;
    public int Spd;
}

Missing Fields

This solution relies on the 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 stores the json schema of CharaData in a lazy fashion
  • Validate compares the json against the schema and if there is a mismatch then it throws a JSchemaValidationException
    • It exposes a property which type is ValidationError which contains a lots of information about the mismatch

Extra fields

This solution relies on 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}");
    }
}
  • I've defined the CharaData as class to be able to derive from it << CharaDataExtras
  • Every extra field will be put into the ExtraFields dictionary

Usage

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

The output:

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