Detect if deserialized object is missing a field with the JsonConvert class in Json.NET
The Json.Net serializer has a MissingMemberHandling
setting which you can set to Error
. (The default is Ignore
.) This will cause the serializer to throw a JsonSerializationException
during deserialization whenever it encounters a JSON property for which there is no corresponding property in the target class.
static void Main(string[] args)
{
try
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MissingMemberHandling = MissingMemberHandling.Error;
var goodObj = JsonConvert.DeserializeObject<MyJsonObjView>(correctData, settings);
System.Console.Out.WriteLine(goodObj.MyJsonInt.ToString());
var badObj = JsonConvert.DeserializeObject<MyJsonObjView>(wrongData, settings);
System.Console.Out.WriteLine(badObj.MyJsonInt.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
}
}
Result:
42
JsonSerializationException: Could not find member 'SomeOtherProperty' on object
of type 'MyJsonObjView'. Path 'SomeOtherProperty', line 3, position 33.
See: MissingMemberHandling setting.
Using JsonConvert.DeserializeObject, how to warn about missing members, but continue deserializing?
You can do something like this:
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
namespace NewtonsoftJsonSample
{
public static class Program
{
public static void Main(string[] args)
{
var json = "{'name': 'john', 'age': 45, 'city': 'Bristol'}".Replace("'", "\"");
// remember to set an error handler and to raise an error each time a member is missing
// during deserialization
var settings = new JsonSerializerSettings
{
Error = OnError,
MissingMemberHandling = MissingMemberHandling.Error
};
var deserialized = JsonConvert.DeserializeObject<Character>(json, settings);
Console.WriteLine("Deserialized object: {0}", deserialized);
Console.ReadLine();
static void OnError(object sender, ErrorEventArgs args)
{
Console.WriteLine("Unable to find member '{0}' on object of type {1}", args.ErrorContext.Member, args.ErrorContext.OriginalObject.GetType().Name);
// set the current error as handled
args.ErrorContext.Handled = true;
}
}
}
public class Character
{
public string Name { get; set; }
public override string ToString()
{
return $"Name: {this.Name}";
}
}
}
Basically you need to use both MissingMemberHandling
and Error
properties of JsonSerializerSettings
class.
Json.NET : Detect an absence of a property on Json which appears to be a member of my object
Json.Net
provides a way to achieve that.
You can set an attribute on the property in your Model class. and if
that property is not available in JSON it'll throw an exception.
Here is the Example
Model
public class Videogame
{
[JsonProperty(Required = Required.Always)]
public string Name { get; set; }
[JsonProperty(Required = Required.AllowNull)]
public DateTime? ReleaseDate { get; set; }
}
Test Code
string json = @"{
'Name': 'Starcraft III'
}";
Videogame starcraft = JsonConvert.DeserializeObject<Videogame>(json);
You can read more about this here
How to check for missing field in JSON using JSON.NET
The simplest option would be to make it an int?
property, then check whether the value is null afterwards:
public class SomeClass
{
public string Foo { get; set; }
public int? Bar { get; set; }
}
...
var deserialized = JsonConvert.DeserializeObject<SomeClass>(json);
if (deserialized.Bar == null)
{
// Whatever you want to do if it wasn't set
}
Of course, the JSON could explicitly set the value to null, but I expect you'd probably be happy handling that as if it was missing.
How to get all not exist keys after JsonConvert DeserializeObject in Json.Net?
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
How to configure JSON.net deserializer to track missing properties?
Another way to find null/undefined tokens during De-serialization is to write a custom JsonConverter
, Here is an example of custom converter which can report both omitted tokens (e.g. "{ 'Id':5 }"
) and null tokens (e.g {"Id":5,"SomeString":null,"SomeInt":null}
)
public class NullReportConverter : JsonConverter
{
private readonly List<PropertyInfo> _nullproperties=new List<PropertyInfo>();
public bool ReportDefinedNullTokens { get; set; }
public IEnumerable<PropertyInfo> NullProperties
{
get { return _nullproperties; }
}
public void Clear()
{
_nullproperties.Clear();
}
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
existingValue = existingValue ?? Activator.CreateInstance(objectType, true);
var jObject = JObject.Load(reader);
var properties =
objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var property in properties)
{
var jToken = jObject[property.Name];
if (jToken == null)
{
_nullproperties.Add(property);
continue;
}
var value = jToken.ToObject(property.PropertyType);
if(ReportDefinedNullTokens && value ==null)
_nullproperties.Add(property);
property.SetValue(existingValue, value, null);
}
return existingValue;
}
//NOTE: we can omit writer part if we only want to use the converter for deserializing
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var objectType = value.GetType();
var properties =
objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
writer.WriteStartObject();
foreach (var property in properties)
{
var propertyValue = property.GetValue(value, null);
writer.WritePropertyName(property.Name);
serializer.Serialize(writer, propertyValue);
}
writer.WriteEndObject();
}
}
Note: we can omit the Writer part if we don't need to use it for serializing objects.
Usage Example:
class Foo
{
public int Id { get; set; }
public string SomeString { get; set; }
public int? SomeInt { get; set; }
}
class Program
{
static void Main(string[] args)
{
var nullConverter=new NullReportConverter();
Console.WriteLine("Pass 1");
var obj0 = JsonConvert.DeserializeObject<Foo>("{\"Id\":5, \"Id\":5}", nullConverter);
foreach(var p in nullConverter.NullProperties)
Console.WriteLine(p);
nullConverter.Clear();
Console.WriteLine("Pass2");
var obj1 = JsonConvert.DeserializeObject<Foo>("{\"Id\":5,\"SomeString\":null,\"SomeInt\":null}" , nullConverter);
foreach (var p in nullConverter.NullProperties)
Console.WriteLine(p);
nullConverter.Clear();
nullConverter.ReportDefinedNullTokens = true;
Console.WriteLine("Pass3");
var obj2 = JsonConvert.DeserializeObject<Foo>("{\"Id\":5,\"SomeString\":null,\"SomeInt\":null}", nullConverter);
foreach (var p in nullConverter.NullProperties)
Console.WriteLine(p);
}
}
Throwing exception on deserializing type/naming-faulty JSON?
You could use Json.NET Schema at https://www.newtonsoft.com/jsonschema
This is from its home page:
JSchema schema = JSchema.Parse(@"{
'type': 'object',
'properties': {
'name': {'type':'string'},
'roles': {'type': 'array'}
}
}");
JObject user = JObject.Parse(@"{
'name': 'Arnie Admin',
'roles': ['Developer', 'Administrator']
}");
bool valid = user.IsValid(schema);
// true
How to set the value while deserialization if property not included in json string using custom JsonConverter
You can create a custom converter where you let Newtonsoft read and parse all the properties that are present in the json and you can manually assign values to the missing ones:
public class TestClassConverter : JsonConverter<TestClass>
{
private static readonly Random rand = new Random();
public override TestClass ReadJson(JsonReader reader, Type objectType, TestClass existingValue, bool hasExistingValue, JsonSerializer serializer)
{
//Let Newtonsoft do the heavy lifting
var jObject = JObject.Load(reader);
var target = new TestClass();
serializer.Populate(jObject.CreateReader(), target);
//Set the intact property manually
target.InternalID = rand.Next();
return target;
}
public override void WriteJson(JsonWriter writer, TestClass value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
I've used random, but you can assign whatever value you want.
Usage:
var data = JsonConvert.DeserializeObject<List<TestClass>>(json, converters: new TestClassConverter());
With this approach you don't have to decorate TestClass
's InternalID
property with a JsonConverterAttribute
.
UPDATE: Non-generic JsonConverter version
public class TestClassConverter : JsonConverter
{
private static readonly Random rand = new Random();
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TestClass);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var target = new TestClass();
serializer.Populate(jObject.CreateReader(), target);
target.InternalID = rand.Next();
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Related Topics
Remove Element of a Regular Array
How to Rethrow an Exception in C#
How to Get Httpclient to Pass Credentials Along With the Request
Ca2202, How to Solve This Case
Mssql Error 'The Underlying Provider Failed on Open'
When Is It Acceptable to Call Gc.Collect
What Are the Pros and Cons to Keeping SQL in Stored Procs Versus Code
Check If a Class Is Derived from a Generic Class
Deserializing Json to .Net Object Using Newtonsoft (Or Linq to Json Maybe)
How to Check If a Given String Is a Legal/Valid File Name Under Windows
How to Round a Number to Two Decimal Places in C#
How to Remove All Namespaces from Xml With C#
How to Speed Up the Build and Run Process in Unity for Mobile Devices iOS/Android