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;
}
}
}
}
Related
I'm currently using Newtonsoft's json library to deserialize json from a file.
JSON:
{
"name":"test",
"description":"test",
"tags":"Maps"
}
C#:
public class RootObject
{
public string name { get; set; }
public string tag { get; set; }
public string description { get; set; }
public string[] tags { get; set; }
}
The "tags" has to be an array as it is required for steam.
How can I retrieve the "tags" as an string array?
The console prints out this expection:
Newtonsoft.Json.JsonSerializationException: Error converting value "Maps" to type 'System.String[]'. Path 'tags', line 4, position 17
Your json property "tags" doesn't contain an array, but just a string. Change "Maps" to ["Maps"].
What you need is a Custom JsonConverter.
public class StringOrArrayToStringConveter<T> : JsonConverter
The meat of the solution is the override of the ReadJson which you need to return an array of T. Then you can use it like
[JsonConverter(typeof(StringOrArrayToStringConveter<string>))]
public string[] tags { get; set; }
Hope that put you in a good starting point.
EDIT:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ConsoleApp1
{
public class RootObject
{
public string name { get; set; }
public string tag { get; set; }
public string description { get; set; }
[JsonConverter(typeof(StringOrArrayToStringConveter<string>))]
public string[] tags { get; set; }
}
class Program
{
static void Main(string[] args)
{
var data1 = "{\"name\":\"test\", \"description\":\"test\",\"tags\":\"Maps\"}";
var deserialized1 = JsonConvert.DeserializeObject<RootObject>(data1);
var data2 = "{\"name\":\"test\", \"description\":\"test\",\"tags\":[\"Maps\", \"Maps2\"]}";
var deserialized2 = JsonConvert.DeserializeObject<RootObject>(data2);
}
}
public class StringOrArrayToStringConveter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object returnValue = new Object();
if (reader.TokenType == JsonToken.StartArray)
{
returnValue = JToken.Load(reader).ToObject<T[]>();
}
else if (reader.TokenType == JsonToken.String)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
returnValue = new List<T>() { instance }.ToArray();
}
return returnValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
I would suggest you change Tag into Dictionary e.g. public Dictionary tags { get; set; }
public class RootObject
{
public string name { get; set; }
public string tag { get; set; }
public string description { get; set; }
public string[] tags { get; set; }
public Dictionary<string, Tag> tags { get; set; }
}
public Tag
{
public string name{ get; set; }
}
{
"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[];
}
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]);
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
I have problem when trying to deserialize a json from internet.
But I don't know how to deserialize difference types in one array correctly.
How to do it correctly?
Sorry for my poor english.
Here is json:
{
"timestamp":"2012-06-19T08:00:49Z",
"items":[
{
"type":"text",
"content":"etc"
},
{
"type":"video",
"url":"etc"
}
...
]
}
My code:
public interface IPost
{
string PostType { get; set; }
}
public class TextPost : IPost
{
[JsonProperty("type")]
public string PostType { get; set; }
[JsonProperty("content")]
public string Content { get; set; }
}
public class VideoPost : IPost
{
[JsonProperty("type")]
public string PostType { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
}
public class ResponseData
{
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
[JsonProperty("items")]
public List<IPost> Items { get; set; }
}
I figured out a way to parse it. By using JsonConverter.
Here is my Converter class:
public class PostsConverter : JsonConverter
{
// Create an instance
protected IPost Create(JObject jObject)
{
if (PostType("Text", jObject)) return new Text();
if (PostType("Video", jObject)) return new Video();
throw new Exception("Error");
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var list = new List<IPost>();
var arr = JArray.Load(reader);
foreach (var item in arr)
{
var obj = JObject.Load(item.CreateReader());
// Create target object based on JObject
var post = Create(obj);
// Populate the object properties
serializer.Populate(obj.CreateReader(), post);
list.Add(post);
}
return list;
}
private bool PostType(string type, JObject jObject)
{
return jObject["PostType"] != null && jObject["PostType"].Value<string>() == type;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// I don't need serialize object
throw new NotImplementedException();
}
}
Response class:
public class ResponseData
{
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
[JsonConverter(typeof(PostsConverter))]
[JsonProperty("items")]
public List<IPost> Items { get; set; }
}
Now I can parse it:
var myData = JsonConvert.DeserializeObject<ResponseData>(json);