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

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?

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

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

Deserialize JSON to list with unknown object name in C#

I want to deserialize following JSON.
The problem is that the objects "ANDE" & "DAR" can change.
Means the objects are unknown and change depending on the JSON i wanna deserialize.
About 8000 different objects (ANDE, DAR, ...) need to be deserialized.
{"ANDE":
{"chart":[
{"date":"20180914","minute":"09:30"},{"date":"20180914","minute":"13:30"}]},
"DAR":
{"chart":[
{"date":"20180914","minute":"09:30"},{"date":"20180914","minute":"13:30"}]}}
I get the data by HTTP response and want to put into a List:
HttpResponseMessage response = client.GetAsync(API_PATH).GetAwaiter().GetResult();
List historicalDataList = response.Content.ReadAsAsync<List<HistoricalDataResponse>>().GetAwaiter().GetResult();
The HistoricalDataResponse class looks like:
public class HistoricalDataResponse
{
public string date { get; set; }
public string minute { get; set; }
}
How can i deserialize this kind of JSON with unknown objects in C#?
Any help is highly appreciated.
Then you should use a dynamic variable:
dynamic ReturnValue = JsonConvert.DeserializeObject(jsonstring);
note that as in dynamic objects, properties are determined after being assigned in runtime, so you will not get a drop down menu in design time, and also as its properties are unknown in design time, and property you test in design time even if its not correct, you wont get an error, and you will get the error in runtime when it is assigned.
dynamic ReturnValue = JsonConvert.DeserializeObject(jsonstring);
try
{
var a = ReturnValue.ANDE; // will work if it has ANDE property.
// do what you would do with ANDE
}
catch{}
try
{
var a = ReturnValue.DAR; // will work if it has DAR property.
// do what you would do with DAR
}
catch{}
Use a dictionary with string as key type :
void Main()
{
var client = new HttpClient();
HttpResponseMessage response = client.GetAsync("url").GetAwaiter().GetResult();
var json = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var result = JsonConvert.DeserializeObject<Dictionary<string, DateResponse>>(json);
foreach (var element in result)
{
var key = element.Key; // ANDE
foreach (var item in element.Value.Chart)
{
var date = item.date;
var minute = item.minute;
}
}
}
public class DateResponse{
public List<HistoricalDataResponse> Chart { get; set; }
}
public class HistoricalDataResponse
{
public string date { get; set; }
public string minute { get; set; }
}

Detecting mismatch between constructor parameter names and property names with immutable objects

Consider the following mutable object:
class SomePoco
{
public int Id{get;set;}
public string Name{get;set;}
}
Let's round trip it through Json.NET:
var p=new SomePoco{Id=4,Name="spender"};
var json=JsonConvert.SerializeObject(p);
var pr = JsonConvert.DeserializeObject<SomePoco>(json);
Console.WriteLine($"Id:{pr.Id}, Name:{pr.Name}");
All is good.
Now, let's make out POCO immutable and feed values via a constructor:
class SomeImmutablePoco
{
public SomeImmutablePoco(int id, string name)
{
Id = id;
Name = name;
}
public int Id{get;}
public string Name{get;}
}
... and round-trip the data again:
var p = new SomeImmutablePoco(5, "spender's immutable friend");
var json = JsonConvert.SerializeObject(p);
var pr = JsonConvert.DeserializeObject<SomeImmutablePoco>(json);
Console.WriteLine($"Id:{pr.Id}, Name:{pr.Name}");
Still good.
Now, let's make a small change to our immutable class by renaming a constructor parameter:
class SomeImmutablePoco
{
public SomeImmutablePoco(int pocoId, string name)
{
Id = pocoId;
Name = name;
}
public int Id{get;}
public string Name{get;}
}
then:
var p = new SomeImmutablePoco(666, "diabolo");
var json = JsonConvert.SerializeObject(p);
var pr = JsonConvert.DeserializeObject<SomeImmutablePoco>(json);
Console.WriteLine($"Id:{pr.Id}, Name:{pr.Name}");
Oh dear... It looks like Json.NET is doing some reflective magic over the names of our constructor parameters and matching them to property names in our POCO/json. This means that our freshly deserialized object doesn't get an Id assigned to it. When we print out the Id, it's 0.
This is bad, and particularly troublesome to track down.
This problem might exist in a large collection of POCOs. How can I automate finding these problem POCO classes?
Here is the code that finds such classes using reflection:
var types = new List<Type>() { typeof(SomeImmutablePoco) }; // get all types using reflection
foreach (var type in types)
{
var props = type.GetProperties(bindingAttr: System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (var ctor in type.GetConstructors())
{
foreach (var param in ctor.GetParameters())
{
if (!props.Select(prop => prop.Name.ToLower()).Contains(param.Name.ToLower()))
{
Console.WriteLine($"The type {type.FullName} may have problems with Deserialization");
}
}
}
}
You can map your json property like that:
class SomeImmutablePoco
{
public SomeImmutablePoco(int pocoId, string name)
{
Id = pocoId;
Name = name;
}
[JsonProperty("pocoId")]
public int Id { get; }
public string Name { get; }
}

How can i see if my JSON contains a certain value and then compare it?

var Name = "Resources.myjson.json";
var NameJSON = new System.IO.StreamReader(typeof(Strings).GetTypeInfo().Assembly.GetManifestResourceStream(Name)).ReadToEnd();
var ParsedBrandJSON = Newtonsoft.Json.JsonConvert.DeserializeObject<TheInfo>(NameJSON);
await JsonCS.LoadJson(ParsedBrandJSON);
And on the page:
static public class TheInfoJSON
{
static public TheInfo Data { get; set; }
static public async Task LoadJson(Data JSON)
{
Data = JSON;
}
}
and
public class TheInfo
{
public List<TheName> TheName { get; set; } = new List<TheName>();
}
My json:
{
"TheInfo":[
{
"TheName": ["Martin", "Jonas", "Alex", "Oscar"]
}
]
}
When i now try to compare how can i see if my JSON contains a certain object and then store that as a single TheName? Is it possible to do it in the same cast?
var TheNameDataFromOtherPage = OtherPage.TheName; //Here i gather the name from another page that i will compare with the JSON
//Wrong syntax
bool DoTheyMatch = TheNameDataFromOtherPage == TheInfoJSON.Data.TheName.Contains("Alex");
This is now wrong syntax because i cant compare the value to a bool. How can i get out the data i find and then instead of having TheInfoJSON.Data.TheName.Contains("Alex"); as a bool, back to a single value of TheName containing "Alex" so I can create a bool out of the two values to see if the JSON has it or not.
I tried to add something along the lines like this after the contains(): as TheInfo.TheName but that isnt the correct syntax either.
bool DoTheyMatch = TheInfoJSON.Data.TheName.Contains(TheNameDataFromOtherPage);

ISerializable - serialise a recursive object type eg, a Map

I am trying to create a web service using WCF and C# to present some data to an AJAX client.
I would like my data to return like so (JSON):
{"Settings":{"LAN":{"IPAddress":"10.0.0.1", "SubnetMask":"255.255.255.0"},"WAN":{"Status":"Up"}}}
I have created a simple JsonMap class as such:
[Serializable]
public class JsonMap :ISerializable
{
Dictionary<string, JsonMap> children { get; set; }
public string Value { get; set; }
public JsonMap()
{
this.children = new Dictionary<string, JsonMap>();
this.Value = string.Empty;
}
public JsonMap this[string key]
{
get
{
if (!this.children.ContainsKey(key))
this.children[key] = new JsonMap();
return this.children[key];
}
set
{
this.children[key] = value;
}
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (string key in this.children.Keys)
if (this[key].children.Keys.Count == 0)
info.AddValue(key, this[key].Value);
else
info.AddValue(key, this[key]);
}
}
In theory, as the JsonMap class is serialized it should check if the children are also parents, and render them if so - or render the child's value.
However - when running this through WCF it crashed out and I get no-data returned.
Am I missing something obvious here?
This is how I serialize to Json, it doesnt answer your question and may be useless, but may help.. so just in case:
System.Web.Script.Serialization.JavaScriptSerializer o = new System.Web.Script.Serialization.JavaScriptSerializer();
string JsonString = o.Serialize(MyObject);

Storing/restoring values from a class to/from a list of property names and values

I am not sure what the best and simplest way to do this, so any advice is appreciated.
I want to get all the fields on any/all/single domain entity class and add prefix/remove prefix dynamically when calling a particular method.
For example, I have entities such as:
public class Shop
{
public string TypeOfShop{get;set}
public string OwnerName {get;set}
public string Address {get;set}
}
public class Garage
{
public string Company {get;set}
public string Name {get;set}
public string Address {get;set}
}
and so on...
I want to get a list of the properties with a prefix:
public Class Simple
{
public class Prop
{
public string Name{get;set;}
public string Value{get;set;}
}
public ICollection list = new List<Prop>();
//set all prop
public void GetPropertiesWithPrefix(Garage mygarage, string prefix)
{
list.Add(new Prop{Name = prefix + "_Company", Value = mygarage.Company});
//so on... upto 50 props...
}
}
//to get this list I can simple call the list property on the Simple class
When reading each field I am using a switch statement and setting the value.
//Note I return a collection of Prop that have new values set within the view,lets say
//this is a result returned from a controller with the existing prop names and new values...
public MyGarage SetValuesForGarage(MyGarage mygarage, string prefix, ICollection<Prop> _props)
{
foreach (var item in _prop)
{
switch(item.Name)
{
case prefix + "Company":
mygarage.Company = item.Value;
break;
//so on for each property...
}
}
}
Is there a better, simpler or more elegant way to do this with linq or otherwise?
You could store props in a dictionary, then have:
mygarage.Company = _props[prefix + "_Company"];
mygarage.Address = _props[prefix + "_Address"];
//And so on...
in your SetValuesForGarage method instead of a loop with a switch inside.
EDIT
For more info on using Dictionary see MSDN.
You can define list something like:
Dictionary<string, string> list = new Dictionary<string, string>();
And have something like the following in your GetPropertiesWithPrefix method:
list.Add(prefix + "_Company", mygarage.Company);
list.Add(prefix + "_Address", mygarage.Address);
//And so on...
This would eliminate your Prop class.
Maybe the following method works for you. It takes any object, looks up its properties and returns a list with your Prop objects, each for every property.
public class PropertyReader
{
public static List<Prop> GetPropertiesWithPrefix(object obj, string prefix)
{
if (obj == null)
{
return new List<Prop>();
}
var allProps = from propInfo
in obj.GetType().GetProperties()
select new Prop()
{
Name = prefix + propInfo.Name,
Value = propInfo.GetValue(obj, null) as string
};
return allProps.ToList();
}
}

Categories