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 noSpd
key in json data but in structure there is aSpd
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:
- Find the missing fields
- 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 ofCharaData
in a lazy fashionValidate
compares thejson
against the schema and if there is a mismatch then it throws aJSchemaValidationException
- It exposes a property which type is
ValidationError
which contains a lots of information about the mismatch
- It exposes a property which type is
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