C# deserialize Json based on condition - c#

{
"timeAgo": "6 minutes ago",
"time": "07/11/2016 07:00 AM",
"alertId": 145928,
"details": {
},
"priority": 10,
"type": 2,
"isClosed": 0,
"notesCount": 0,
"patientAccountId": 680,
"isRead": 0
}
I want to deserialize the json based on the int value 'type', in such as way, I want the details to be different types
public class Notification
{
public string timeAgo { get; set; }
public string time { get; set; }
public int alertId { get; set; }
public object details { get; set; }
public int priority { get; set; }
public int type { get; set; }
public int isClosed { get; set; }
public int notesCount { get; set; }
public int patientAccountId { get; set; }
public int isRead { get; set; }
}
if type = 1, then the object 'details' is of type A, if type = 2, 'details' is of type B and so on. There are about 25 values for type.
So, later I can do something like:
Notification n = ....
if (type == 1)
{
A a = (a) n.details;

If your json does not have appropriate typing included in the JSON, this will work.
This may need tweaking if your actual structure is more complex, but I managed to get this to work on your sample.
var instance = Newtonsoft.Json.JsonConvert.DeserializeObject<Notification>(
js,
new ItemConverter());
public class ItemA : Item { }
public class ItemB : Item { }
public class Item { }
public class Notification
{
public string timeAgo { get; set; }
public string time { get; set; }
public int alertId { get; set; }
public Item details { get; set; }
public int priority { get; set; }
public int type { get; set; }
public int isClosed { get; set; }
public int notesCount { get; set; }
public int patientAccountId { get; set; }
public int isRead { get; set; }
}
public class ItemConverter : JsonConverter
{
private Type currentType;
public override bool CanConvert(Type objectType)
{
return typeof(Item).IsAssignableFrom(objectType) || objectType == typeof(Notification);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject item = JObject.Load(reader);
if (item["type"] != null)
{
// save the type for later.
switch (item["type"].Value<int>())
{
case 1:
currentType = typeof(ItemA);
break;
default:
currentType = typeof(ItemB);
break;
}
return item.ToObject<Notification>();
}
// use the last type you read to serialise.
return item.ToObject(currentType);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

You can use special JSON serializer settings, like
var jsonSerializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
};
Type name will be stored with actual "details" data, so you can deserialize it to valid class.
var instance = new Notification
{
details = new Details
{
Name = "Hello"
}
};
var json = JsonConvert.SerializeObject(instance, jsonSerializerSettings);
Produced json
{"$type":"Application.Notification, Application","details":{"$type":"Application.Details, Application","Name":"Hello"}}

var json = "{\r\n \"timeAgo\": \"6 minutes ago\",\r\n \"time\": \"07/11/2016 07:00 AM\",\r\n \"alertId\": 145928,\r\n \"details\": {\r\n\r\n\r\n },\r\n \"priority\": 10,\r\n \"type\": 2,\r\n \"isClosed\": 0,\r\n \"notesCount\": 0,\r\n \"patientAccountId\": 680,\r\n \"isRead\": 0\r\n }";
Notification data = JsonConvert.DeserializeObject<Notification>(json);
var type=1;
if (type == 1)
{
string[] a = data.details as string[];
}

Related

Call JsonConvert.DeserializeObject when you dont know the data type?

I have a 3rdParty program that I receive JOSN data from. The Json can come in two ways and I have created two classes to represent the two JSON messages using http://json2csharp.com/
public class Data1
{
public Content1 content { get; set; }
public string name { get; set; }
public string address { get; set; }
public string age { get; set; }
}
public class Data2
{
public Content2 content { get; set; }
public string name { get; set; }
public string address { get; set; }
public string age { get; set; }
}
They are the same apart from Content1 and Content2 (I have classes for them also).
I want to deserialize the data like so:
var object1 = JsonConvert.DeserializeObject<Data1>(message1);
var object2 = JsonConvert.DeserializeObject<Data2>(message2);
But I do not know what message I will receive so how can I create one class that I can deserialize to and then use.
I have looked at this example https://www.jerriepelser.com/blog/deserialize-different-json-object-same-class/ and have attempted to create a SuperClass that has a:
[JsonConverter(typeof(SuperData))]
but I'm not sure how to implement the ReadJson function.
Do I check the data that comes in and detect if content is type Content1 or Content2? If so how do I do this?
How do I then know what class has been deserialized so I can then know to check for the different content?
Here is the json:
Data1
{
"content": {
"person_id": "1234",
"timestamp": "2018-10-30 13:09:04.382885",
"features": "old, cranky"
},
"name": "JoeBloggs",
"address": "address1",
"age": 31,
}
Data2
{
"content":
{
"detections": 1,
"detection_boxes": [
{
"name": "JoeBloggs",
"bbox": [
1988.162842886987,
-20.466329557695307,
2662.417876180017,
846.0808020362452
],
"id": 3610
}
],
"timestamp": "2018-10-30 13:09:07.171000",
]
},
"name": "JoeBloggs",
"address": "address1",
"age": 31,
}
I have tried this:
[JsonConverter(typeof(MyDataConverter))]
public class MegaData
{
public object content { get; set; }
public string name { get; set; }
public string address { get; set; }
public string age { get; set; }
}
MyDataConverter has the following:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var content = jObject["message_content"];
if (content["person_id"] != null)
{
return jObject.ToObject<Data1>();
}
else
{
return jObject.ToObject<Data2>();
}
}
Then I attempt to deserialize it as follows:
var alert = JsonConvert.DeserializeObject<MegaData>(message1); <-- EXCEPTION
**Unable to cast object of type 'Data1' to type 'MegaData'.**
What should my MegaData class look like for this to work??
Why can't you have a single class with both type and deserialize to that like
public class DetectionBox
{
public string name { get; set; }
public List<double> bbox { get; set; }
public int id { get; set; }
}
public class Content
{
public int detections { get; set; }
public List<DetectionBox> detection_boxes { get; set; }
public string timestamp { get; set; }
public string person_id { get; set; }
public string features { get; set; }
}
public class Data
{
public Content content { get; set; }
public string name { get; set; }
public string address { get; set; }
public int age { get; set; }
}
Data data = JsonConvert.DeserializeObject<Data>(json);
So you can create a custom converter derived from JsonConverter and in the ReadJson you'd do something like this:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var content = jObject["content"];
if (content["person_id"] != null)
{
return jObject.ToObject<Data1>();
}
else
{
return jObject.ToObject<Data2>();
}
}
If you aren't actually writing JSON, you can skip the WriteJson (have it throw a NotImplementedException) method and set CanWrite to return false (so it won't ever be called anyway).

Handling JSON single object and array

I'm using Newtonsoft.Json to work with some JSON data that is being returned to me. Depending on what I request I can either get back something that looks like:
{
"TotalRecords":2,
"Result":
[
{
"Id":24379,
"AccountName":"foo"
},
{
"Id":37209,
"AccountName":"bar"
}
],
"ResponseCode":0,
"Status":"OK",
"Error":"None"
}
or
{
"Result":
{
"Id":24379,
"AccountName":"foo"
},
"ResponseCode":0,
"Status":"OK",
"Error":"None"
}
So sometimes "Result" is an array of Results or "Result" could be a single response.
I've tried using the answer from How to handle both a single item and an array for the same property using JSON.net but I still get errors.
In particular I'm getting a
Newtonsoft.json.jsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List'...
Custom converter looks like:
public class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objecType)
{
return (objecType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objecType, object existingValue,
JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
My response class(es) look like
public class TestResponse
{
[JsonProperty("Result")]
[JsonConverter(typeof(SingleOrArrayConverter<string>))]
public List<DeserializedResult> Result { get; set; }
}
public class DeserializedResult
{
public string Id { get; set; }
public string AccountName { get; set; }
}
And finally my request looks like
List<TestResponse> list = JsonConvert.DeserializeObject<List<TestResponse>>(response.Content);
Your code is fine, it just needs a few type tweaks.
This line
List<TestResponse> list = JsonConvert.DeserializeObject<List<TestResponse>>(response.Content);
needs to be like this, because your response is an object, not a List.
TestResponse list = JsonConvert.DeserializeObject<TestResponse>(response);
Then your custom deserializer attribute:
[JsonConverter(typeof(SingleOrArrayConverter<string>))]
needs to become:
[JsonConverter(typeof(SingleOrArrayConverter<DeserializedResult>))]
because your Result object is not a string or an array of strings, it's either an array of DeserializedResults or a DeserializedResult.
I think, that there is no way to understend which type of response do you have due desirialization. Thats why i propose to check manualy type of response:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace TestConsoleApp
{
public class Class1
{
public class Result
{
public int Id { get; set; }
public string AccountName { get; set; }
}
public class ModelWithArray
{
public int TotalRecords { get; set; }
public List<Result> Result { get; set; }
public int ResponseCode { get; set; }
public string Status { get; set; }
public string Error { get; set; }
}
public class Result2
{
public int Id { get; set; }
public string AccountName { get; set; }
}
public class ModelWithoutArray
{
public Result2 Result { get; set; }
public int ResponseCode { get; set; }
public string Status { get; set; }
public string Error { get; set; }
}
public static void Main(params string[] args)
{
//string json = "{\"TotalRecords\":2,\"Result\":[{\"Id\":24379,\"AccountName\":\"foo\"},{\"Id\":37209,\"AccountName\":\"bar\"}], \"ResponseCode\":0,\"Status\":\"OK\",\"Error\":\"None\"}";
string json = "{\"Result\":{\"Id\":24379,\"AccountName\":\"foo\"},\"ResponseCode\":0,\"Status\":\"OK\",\"Error\":\"None\"}";
if (checkIsArray(json))
{
ModelWithArray data = JsonConver.DeserializeObject<ModelWithArray >(json);
}else
{
ModelWithoutArray data = JsonConver.DeserializeObject<ModelWithoutArray>(json);
}
}
static bool checkIsArray(string json)
{
Dictionary<string, object> desData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
if (desData["Result"].GetType().Name.Contains("Array"))
{
return true;
}
else
{
return false;
}
}
}
}

How to deserialize a JSON array containing different data types to a single object

Over the last few days I have been researching on how to deserialize a JSON response to and C# object. The JSON is valid but I can not get any JSON to C# converters to convert it. I also cannot find an answer on Stackoverflow that works for this instance.
The JSON Response is:
[
{
"SEX": "Male",
"BREED": "Opifex",
"PVPRATING": 1301,
"NAME": "Kilmanagh",
"FIRSTNAME": "Big",
"PVPTITLE": "Freshman",
"LASTNAME": "Kahuna",
"CHAR_DIMENSION": 5,
"ALIENLEVEL": 30,
"RANK_name": "Vindicator",
"HEADID": 40281,
"PROFNAME": "Guru",
"LEVELX": 220,
"PROF": "Martial Artist",
"CHAR_INSTANCE": 12734,
"SIDE": "Omni"
},
{
"ORG_DIMENSION": 5,
"RANK_TITLE": "President",
"ORG_INSTANCE": 9911,
"NAME": "Elements of Destruction",
"RANK": 0
},
"2016/04/06 08:37:26"
]
From my inspection it is an array that contains two objects and a string.
I have used the following to attempt to convert it to an object:
resultArray = JsonConvert.DeserializeObject<List<JsonWhoisResult>>(data);
and
result = JsonConvert.DeserializeObject<JsonWhoisResult>(data);
Either way I get an error:
Error converting value ...(snip)... [ConsoleApplication6.JsonWhoisResult]'. Path '', line 1, position 536.`
I do not know if I have the object wrong, or if I am using incorrect code for this JSON format. I am using:
public class JsonWhoisResult
{
public stats stats { get; set; }
public header header { get; set; }
public string datetime { get; set; }
}
public class header
{
public int ORG_DIMENSION { get; set; }
public string RANK_TITLE { get; set; }
public int ORG_INSTANCE { get; set; }
public string NAME { get; set; }
public int RANK { get; set; }
}
public class stats
{
public string SEX { get; set; }
public string BREED { get; set; }
public int PVPRATING { get; set; }
public string NAME { get; set; }
public string FIRSTNAME { get; set; }
public string PVPTITLE { get; set; }
public string LASTNAME { get; set; }
public int CHAR_DIMENSION { get; set; }
public int ALIENLEVEL { get; set; }
public string RANK_name { get; set; }
public int HEADID { get; set; }
public string PROFNAME { get; set; }
public int LEVELX { get; set; }
public string PROF { get; set; }
public int CHAR_INSTANCE { get; set; }
public string SIDE { get; set; }
}
If anyone has any solutions I would really appreciate it. I have several more that use this type of style. If I can get a solution then I should be able to apply it to the rest.
Because your JSON is an array you cannot deserialize into a single JsonWhoisResult, and because your array contains disparate object types, you cannot deserialize directly into a List<JsonWhoisResult>. You will need to make a custom JsonConverter to handle this situation. The converter can use Json.Net's LINQ-to-JSON API to deserialize the JSON, then manually extract each item into its appropriate object type and populate a single JsonWhoisResult as you want. Something like this should work:
class JsonWhoisResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(JsonWhoisResult));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
JsonWhoisResult result = new JsonWhoisResult();
result.stats = array[0].ToObject<stats>();
result.header = array[1].ToObject<header>();
result.datetime = array[2].ToString();
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it like this:
JsonWhoisResult result = JsonConvert.DeserializeObject<JsonWhoisResult>(json, new JsonWhoisResultConverter());
Fiddle: https://dotnetfiddle.net/d1hkCn
You can use JSON.net's support for dynamic to do this. I just tested this with the JSON as you pasted it above, and it works - but note, this is not strongly typed.
dynamic result = JsonConvert.DeserializeObject(json);
// value is "Freshman"
Console.WriteLine("result[0].PVPTITLE = '{0}'", result[0].PVPTITLE);
// value is "President"
Console.WriteLine("result[1].RANK_TITLE = '{0}'", result[1].RANK_TITLE);
// value is 2016-04-06 08:37:27
Console.WriteLine("result[2] '{0}'", (DateTime)result[2]);

Custom JSon convertion to C# objects

I am getting JSon from a third party API which does not match my classes.
Some JSon properties are not be be converted others has different names, etc.
How can I define a custom conversion from the JSON to my C# object.
These are the objects I have in C#:
public class PropertyList {
public Int32 Page { get; set; }
public String[] ErrorMessages { get; set; }
public List<Property> Properties { get; set; }
}
public class Property {
public String Name { get; set; }
public String Reference { get; set; }
public String Region { get; set; }
public IList<String> Features { get; set; }
public String Id { get; set; }
public Double Price { get; set; }
public IList<String> ImagesUrls { get; set; }
}
And this is the JSon data which I want to convert from:
{
"page" : 0,
"errorMessages" : [ ],
"listings" : [
{
"data" : {
"name" : "The name",
"reference__c" : "ref1234",
"region__c" : "London",
"features__c" : "Garage;Garden;",
"id" : "id1234",
"price_pb__c" : 700000,
},
"media" : {
"images" : [
{
"title" : "image1",
"url" : "http://www.domain.com/image1"
},
{
"title" : "image2",
"url" : "http://www.domain.com/image2"
}
]
}
}
{
NOTE: Other items
}
]
}
How should I do this?
UPDATE 1
Using Thiago suggestion I was able to parse Page and ErrorMessages but not Properties. So I create the following converter:
public class PropertyResultConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return (objectType == typeof(PropertyResult));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
JObject jo = JObject.Load(reader);
PropertyResult propertyResult = jo.ToObject<PropertyResult>();
propertyResult.Properties = jo.SelectToken("listings.data").ToObject<List<Property>>();
return propertyResult;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
But when I try this I get the following error:
An exception of type 'System.NullReferenceException' occurred
On:
propertyResult.Properties = jo.SelectToken("listings.data").ToObject<List<Property>>();
Any idea why this happens?
Miguel, have you tried http://www.newtonsoft.com/json ?
You can do the mappings of your code using C# attributes, like the snippets below.
public class PropertyList {
[JsonProperty("page")]
public Int32 Page { get; set; }
[JsonProperty("errorMessages")]
public String[] ErrorMessages { get; set; }
[JsonProperty("listings")]
public List<Property> Properties { get; set; }
}
public class Property {
[JsonProperty("name")]
public String Name { get; set; }
[JsonProperty("reference__c")]
public String Reference { get; set; }
[JsonProperty("region__c")]
public String Region { get; set; }
[JsonProperty("features__c")]
public IList<String> Features { get; set; }
[JsonProperty("id")]
public String Id { get; set; }
[JsonProperty("price_pb__c")]
public Double Price { get; set; }
[JsonProperty("media")]
public IList<String> ImagesUrls { get; set; }
}
I'm not sure if IList will work as you are expecting. I believe you will need to adapt it to Dictionary.
You can take a look on the API:
http://www.newtonsoft.com/json/help/html/SerializationAttributes.htm
UPDATE
Try to parse 'data' using the snippet below:
JObject jsonObject = JObject.Parse(json);
IList<JToken> results = jsonObject["listings"]["data"].Children().ToList();
IList<Property> processedList = new List<Property>();
foreach (JToken item in results)
{
Property propertyItem = JsonConvert.DeserializeObject<Property>(item.ToString());
processedList.Add(propertyItem);
}
I ran that snippet and it worked. You can explore the code that I generated on my github:
https://github.com/thiagoavelino/VisualStudio_C/blob/master/VisualStudio_C/StackOverFlow/ParsingJason/ParsingJson.cs

Deserialize Json objects with Json.Net

I'm trying to deserialize an object with Json.net. I was able to do it successfully but its more of a hack so I'm looking for a better/proper way to do so.
{
"page":"admin",
"context":"id",
"records":{
"rCount":6,
"1":[
{
"name":"Romeo",
"dob":"01\/01\/1970"
},
{
"name":"Harry",
"dob":"10\/10\/2012"
},
{
"name":"Lee",
"dob":"17\/10\/2012"
}],
"2":[
{
"name":"Mark",
"dob":"01\/01\/1970"
},
{
"name":"Jack",
"dob":"10\/10\/2012"
},
{
"name":"Json",
"dob":"17\/10\/2012"
}],
}}
this is the json string the issue is with the records object. if it doesn't have that rCount variable then it can be deserialized as a Dictionary but because of the rCount variable it can't be deserialized properly as a dictionary. What should be the proper way of deserialzing this object.
Here is my Solution:
class Program
{
static void Main(string[] args)
{
var recordFile = JsonConvert.DeserializeObject<RecordFile>(Properties.Resources.data);
}
public class RecordFile
{
public string Page { get; set; }
public string Context { get; set; }
public Records Records { get; set; }
}
public class Records
{
public int RCount { get; set; }
[JsonExtensionData]
private Dictionary<string, object> _reocordList;
public List<Record[]> RecordList
{
get
{
if (_reocordList != null && _reocordList.Count > 0)
{
return _reocordList.Values.Select(record => JsonConvert.DeserializeObject<Record[]>(record.ToString())).ToList();
}
return new List<Record[]>();
}
}
}
public class Record
{
public string Name { get; set; }
public string Dob { get; set; }
}
}
You can use jObject to manually parse JSON:
public class RecordFile
{
public string Page { get; set; }
public string Context { get; set; }
public Records Records { get; set; }
}
public class Records
{
public int RCount { get; set; }
public IDictionary<string, List<Record>> RecordsDictionary { get; set; }
}
public class Record
{
public string Name { get; set; }
public string Dob { get; set; }
}
Then:
var jObject = JObject.Parse(\* your json *\);
var recordFile = new RecordFile
{
Page = jObject.Value<string>("page"),
Context = jObject.Value<string>("context"),
Records = new Records
{
RCount = jObject["records"].Value<int>("rCount"),
RecordsDictionary =
jObject["records"].Children<JProperty>()
.Where(prop => prop.Name != "rCount")
.ToDictionary(prop => prop.Name),
prop =>
prop.Value.ToObject<List<Record>>())
}
};
Of course it can be easily to handle cases, when property is not present.
I'm assuming you want to get rid of the Records class completely, ending up with something like this:
public class RecordFile
{
public string Page { get; set; }
public string Context { get; set; }
public Dictionary<string, Record[]> Records { get; set; }
}
public class Record
{
public string Name { get; set; }
public string Dob { get; set; }
}
Since you don't care at all about the records.rCount property from the JSON, you could specify a new error handler to simply ignore the property:
var recordFile = JsonConvert.DeserializeObject<RecordFile>(
jsonString,
new JsonSerializerSettings
{
Error = (sender, args) =>
{
if (args.ErrorContext.Path == "records.rCount")
{
// Ignore the error
args.ErrorContext.Handled = true;
}
}
});
Now when an error is encountered with the property in question, the deserializer will just skip over it. Another option would be to write a custom converter, but that feels like overkill just to ignore one property.
Example: https://dotnetfiddle.net/3svPqk
The other answers posted so far should both work. For completeness, I'll show how you can use a custom JsonConverter to solve this and also simplify your model a bit.
Here is the code for the converter:
class RecordFileConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(RecordFile));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
RecordFile rf = new RecordFile();
rf.Page = (string)jo["page"];
rf.Context = (string)jo["context"];
JObject records = (JObject)jo["records"];
rf.RecordCount = (int)records["rCount"];
rf.Records = records.Properties()
.Where(p => p.Name != "rCount")
.Select(p => p.Value.ToObject<Record[]>())
.ToList();
return rf;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To work with the converter, change your model classes as shown below. (Notice that the RecordFile class has a JsonConverter attribute to link the custom RecordFileConverter to it. Also notice the Records class is deleted, while the RCount and RecordList properties have been moved up to the RecordFile class and renamed.)
[JsonConverter(typeof(RecordFileConverter))]
public class RecordFile
{
public string Page { get; set; }
public string Context { get; set; }
public int RecordCount { get; set; }
public List<Record[]> Records { get; set; }
}
public class Record
{
public string Name { get; set; }
public string Dob { get; set; }
}
Then, deserialize as normal:
var recordFile = JsonConvert.DeserializeObject<RecordFile>(json);
Demo: https://dotnetfiddle.net/jz3zUT

Categories

Resources