MvvmCross: Deserilization Error for JSON - json

I'm trying to create a simple application through Lesson 6 in N+1 Days of MvvmCross application sample. But its failed in SimpleRestService while converting json Data serialization.
private T Deserialize<T>(string responseBody)
{ // Error is here for deserilizing
var toReturn = _jsonConverter.DeserializeObject<T>(responseBody);
return toReturn;
}
My Json data through Browser:
[{"Desc":"All","Id":"0"},{"Desc":"Assigned","Id":"2"},{"Desc":"In Progress","Id":"3"},{"Desc":"Resolved","Id":"4"},{"Desc":"Closed","Id":"5"},{"Desc":"Hold","Id":"6"},{"Desc":"低","Id":"8"},{"Desc":"Waiting Approval","Id":"9"},{"Desc":"Cancelled","Id":"10"},{"Desc":"Not Resolved","Id":"8"}]
My Json data in application at responsebody:
[{\"Desc\":\"All\",\"Id\":\"0\"},{\"Desc\":\"Assigned\",\"Id\":\"2\"},{\"Desc\":\"In Progress\",\"Id\":\"3\"},{\"Desc\":\"Resolved\",\"Id\":\"4\"},{\"Desc\":\"Closed\",\"Id\":\"5\"},{\"Desc\":\"Hold\",\"Id\":\"6\"},{\"Desc\":\"低\",\"Id\":\"8\"},{\"Desc\":\"Waiting Approval\",\"Id\":\"9\"},{\"Desc\":\"Cancelled\",\"Id\":\"10\"},{\"Desc\":\"Not Resolved\",\"Id\":\"8\"}]
Error Message shows as :
{Newtonsoft.Json.JsonSerializationException: Cannot deserialize JSON array (i.e. [1,2,3]) into type 'Book.Core.Services.BookSearchResult'.
The deserialized type must be an array or implement a collection interface like IEnumerable, ICollection or IList.
To force JSON arrays to deserialize add the JsonArrayAttribute to the type. Path '', line 1, position 1.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureArrayContract (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract) [0x00000] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, System.Object existingValue, System.String reference) [0x00000] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, System.Object existingValue) [0x00000] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueNonProperty (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.JsonConverter converter, Newtonsoft.Json.Serialization.JsonContainerContract containerContract) [0x00000] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in :0
at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in :0
at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in :0 etc.. }
My Code part:
Class Declaration:
public class BookSearchItem
{
public string Desc { get; set; }
public string Id { get; set; }
}
public class BookSearchResult
{
public List<BookSearchItem> items { get; set; }
}
Binding Declaration:
public void StartSearchAsync(string whatFor, Action<BookSearchResult> success, Action<Exception> error)
{
string address = string.Format("http://192.168.0.76/eFACiLiTYPhone/MobileService/WinPhoneWCFService.svc/callstatustesting");
_simpleRestService.MakeRequest<BookSearchResult>(address,"GET", success, error);
}
Simple Rest Service For Common:
public class SimpleRestService :ISimpleRestService
{
private readonly IMvxJsonConverter _jsonConverter;
public SimpleRestService(IMvxJsonConverter jsonConverter)
{
_jsonConverter = jsonConverter;
}
public void MakeRequest<T>(string requestUrl, string verb, Action<T> successAction, Action<Exception> errorAction)
{
var request = (HttpWebRequest)WebRequest.Create(requestUrl);
request.Method = verb;
request.Accept = "application/json";
MakeRequest(
request,
(response) =>
{
if (successAction != null)
{
T toReturn;
try
{
toReturn = Deserialize<T>(response);
}
catch (Exception ex)
{
errorAction(ex);
return;
}
successAction(toReturn);
}
},
(error) =>
{
if (errorAction != null)
{
errorAction(error);
}
}
);
}
private void MakeRequest(HttpWebRequest request, Action<string> successAction, Action<Exception> errorAction)
{
request.BeginGetResponse(token =>
{
try
{
using (var response = request.EndGetResponse(token))
{
using (var stream = response.GetResponseStream())
{
var reader = new StreamReader(stream);
successAction(reader.ReadToEnd());
}
}
}
catch (WebException ex)
{
Mvx.Error("ERROR: '{0}' when making {1} request to {2}", ex.Message, request.Method, request.RequestUri.AbsoluteUri);
errorAction(ex);
}
}, null);
}
private T Deserialize<T>(string responseBody)
{
var toReturn = _jsonConverter.DeserializeObject<T>(responseBody);
return toReturn;
}
}

You have to use the correct T for your Json call - you can't simply use BookSearchResult for all Json calls.
You can use tools like http://json2csharp.com/ to generate the CSharp classes for you - e.g.
public class RootObject
{
public string Desc { get; set; }
public string Id { get; set; }
}
which you can then use as:
var myItems = service.Deserialize<List<RootObject>>(jsonText);

Related

How to serialize a class with Newtonsoft.Json.JsonConvert.SerializeObject to change empty List to null variable

How to serialize a class with Newtonsoft.Json.JsonConvert.SerializeObject to change empty List to null variable
my result from serialization
{"images":[]} or {"images":{}}
and would like to get
{"images":null}
my simple class
public class Images
{
[JsonProperty("images", Required = Required.Always, NullValueHandling = NullValueHandling.Include)]
public Images()
{
Parameters = new Parameters(); **// I know I could not initiate here only when I need it but I don't want to do it**
}
[JsonProperty("parameters", Required = Required.Always, NullValueHandling = NullValueHandling.Include)]
public Parameters Parameters { get; set; }
}
public class Parameters
{
[JsonProperty("id", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Include)]
public string Id { get; set; }
}
I did something like that
internal class NullEmptyConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(GuidId);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
return value;
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (JsonConvert.SerializeObject(untypedValue, Formatting.None).ToString() == "[]")
{
serializer.Serialize(writer, null);
return;
}
if (JsonConvert.SerializeObject(untypedValue, Formatting.None).ToString() == "{}")
{
serializer.Serialize(writer, null);
return;
}
serializer.Serialize(writer,untypedValue);
return;
}
}
use
   
[JsonConverter (typeof (NullEmptyConverter))]
immediately before
public Parameters Parameters { get; set; }
It works, but maybe it can be done better, simpler or somewhere else something is wrong here?

Deserializing class with abstract property JSON

public abstract class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
[JsonProperty(Required = Required.Always)]
public string Type { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
I already have a JSON converter working to deserialize such objects based on the value of the Type property.
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
Type t = typeof(T);
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
T target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class PersonConverter : JsonCreationConverter<Person>
{
protected override Person Create(Type objectType, JObject jObject)
{
if(jObject["Type"] == null)
{
throw new ArgumentException();
}
string type = (string)jObject["Type"];
if(type == null)
{
throw new ArgumentException();
}
if(type.Equals("Employee", StringComparison.InvariantCultureIgnoreCase))
{
return new Employee();
}
else if (type.Equals("Artist", StringComparison.InvariantCultureIgnoreCase))
{
return new Artist();
}
return null;
}
}
string json = "[{\"Department\": \"Department1\",\"JobTitle\": \"JobTitle1\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Employee\"},{\"Skill\": \"Drawer\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Artist\"}]";
List<Person> person = JsonConvert.DeserializeObject<List<Person>>(json, new PersonConverter());
The above works well.
Now, I have the following class:
public class City
{
public string Name { get; set; }
public int Population { get; set; }
public Person[] Persons { get; set; }
}
How do I write a converter for this City class that can use the PersonConverter to initialize the Persons property? My initial thought was to extract the Persons part as JObject, and then call Deserialize on it using PersonConverter in its ReadJson method, similar to the sample below.
var p = jObject["Persons"].ToString();
List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(p, new PersonConverter());
But ReadJson throws an exception in the Serializer.Populate method since abstract classes cannot be instantiated.
The following is the JSON string as an example:
string Cityjson = "{\"Name\": \"London\" , \"Population\": \"1000\" , \"Persons\": [{\"Department\": \"Department1\",\"JobTitle\": \"JobTitle1\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Employee\"},{\"Skill\": \"Drawer\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Artist\"}]}";
Approach #1
I solved this by
Marking the Persons property to be ignored in deserialization
[JsonIgnore]
public Person[] Persons { get; set; }
In the Create method, instantiating the City object and using PersonConverter to initialize the Persons property
protected override City Create(Type objectType, JObject jObject)
{
if (jObject["Persons"] == null)
{
throw new ArgumentException();
}
var p = jObject["Persons"].ToString();
List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(p, new PersonConverter());
var city = new City();
city.Persons = persons.ToArray();
return city;
}
The ReadJson method would populate the remaining City properties as usual.
Are there any other approaches?
I think this is the most suitable way
In ReadJson when array is passed it was basically crashing since Jarray is not jboject. So, I updated the ReadJson as follows and it worked.
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
JArray jObject = JArray.Load(reader);
List<T> list = new List<T>();
for (int i = 0; i < jObject.Count(); i++)
{
var p = jObject[i];
JObject ob = p as JObject;
T value = Create(objectType, ob);
serializer.Populate(ob.CreateReader(), value);
list.Add(value);
}
return list.ToArray();
}
else
{
JObject jObject = JObject.Load(reader);
T target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
}
And yes, I don't need a CityConverter. Adding PersonConverter is enough.

Deserialize an object into another similar object of another type json.net

I have a question similar to Cannot deserialize JSON array into type - Json.NET, but I still get errors.
So, I have 3 classes:
public class Class1
{
public string[] P2 { get; set; }
}
public class Class2
{
public Wrapper<string>[] P2 { get; set; }
}
public class Wrapper<T>
{
public T Value { get; set; }
}
I am trying to serialize Class 2 into string and back into Class 1.
Here's how:
Class2 c2 = new Class2
{
P2 = new Wrapper<string>[]
{
new Wrapper<string> { Value = "a" },
new Wrapper<string> { Value = "a" },
},
};
string s = JsonConvert.SerializeObject(c2);
Class1 c1 = (Class1)JsonConvert.DeserializeObject(s, typeof(Class1), new FormatConverter());
FormatConverter class is defined below:
public class FormatConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (objectType == typeof(string[]))
{
List<string> list = new List<string>();
while (reader.Read())
{
if (reader.TokenType != JsonToken.StartObject)
{
continue;
}
Wrapper<string> obj = (Wrapper<string>)serializer.Deserialize(reader, typeof(Wrapper<string>));
list.Add(obj.Value);
}
return list.ToArray();
}
}
public override bool CanConvert(Type type)
{
if (type == typeof(string[]))
{
return true;
}
return false;
}
}
What am I missing? I get following exception:
An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll
Additional information: Unexpected end when deserializing object. Path '', line 1, position 46.
Thanks,
Alex
I managed to find the answer myself. Here's how:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (type == typeof(string[]) && reader.TokenType == JsonToken.StartArray)
{
//remove converter not to trigger a recursive call
var converter = serializer.Converters[0];
serializer.Converters.RemoveAt(0);
//deserialization into correct type
Wrapper<string>[] obj = (Wrapper<string>[])serializer.Deserialize(reader, typeof(Wrapper<string>[]));
//restore converter
serializer.Converters.Add(converter);
if (obj != null)
{
return obj.Select(w => w == null ? null : w.Value).ToArray();
}
return reader.Value;
}
}

System.TypeLoadException using Newtonsoft JSON deserialize

I am truly stumped here. Here is my JSON returned:
{"ResponseData":[{"ClusterID":"c02f1f5c-c61b-4f2c-ab5a-249966b3cdef","ClusterName":"Northeast","Courses":[{"CourseID":"8ab4f2b3-8160-4d7e-b79f-8d8b58926cc0","CourseName":"Home Course","SubCourses":[{"SubCourseName":"SubCourse1","SubCourseNumber":18}]},{"CourseID":"b3223464-333b-4c54-89c2-23908e0510c9","CourseName":"Away Course","SubCourses":[{"SubCourseName":"SubCourse1","SubCourseNumber":19}]}],"IsHomeCluster":true},"ResponseErrors":[]}
This is my code to deserialize:
JArray jArr = (JArray)JsonConvert.DeserializeObject(json);
foreach (var item in jArr) {
foreach (var subitem in item["ResponseData"]) {
Console.WriteLine (subitem ["ClusterID"]);
}
}
Project compiles fine, but when I run it in the simulator, I get this error:
System.TypeLoadException: A type load exception has occurred. at
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize
(Newtonsoft.Json.JsonReader reader, System.Type objectType, Boolean
checkAdditionalContent) [0x00000] in :0 at
Newtonsoft.Json.JsonSerializer.DeserializeInternal
(Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000]
in :0 at
Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader
reader, System.Type objectType) [0x00000] in :0 at
Newtonsoft.Json.JsonConvert.DeserializeObject (System.String value,
System.Type type, Newtonsoft.Json.JsonSerializerSettings settings)
[0x00000] in :0 at
Newtonsoft.Json.JsonConvert.DeserializeObject (System.String value)
[0x00000] in :0 at
AppMultiView.CourseInformationScreen.ViewDidLoad () [0x00029] in
/Users/Dan/Desktop/AppTouch_dev /Screens/CourseInformationScreen.cs:48
at (wrapper managed-to-native)
MonoTouch.ObjCRuntime.Messaging:void_objc_msgSend_IntPtr_bool
(intptr,intptr,intptr,bool) at
MonoTouch.UIKit.UINavigationController.PushViewController
(MonoTouch.UIKit.UIViewController viewController, Boolean animated)
[0x00021] in
/Developer/MonoTouch/Source/monotouch/src/UIKit/UINavigationController.g.cs:176
at AppMultiView.HomeScreen.m__2 (System.Object sender,
System.EventArgs e) [0x00016] in /Users/Dan/Desktop/AppTouch_dev
/Screens/HomeScreen.cs:75 at
MonoTouch.UIKit.UIControlEventProxy.Activated () [0x00000] in
/Developer/MonoTouch/Source/monotouch/src/UIKit/UIControl.cs:30 at
(wrapper managed-to-native)
MonoTouch.UIKit.UIApplication:UIApplicationMain
(int,string[],intptr,intptr) at MonoTouch.UIKit.UIApplication.Main
(System.String[] args, System.String principalClassName, System.String
delegateClassName) [0x0004c] in
/Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
at AppMultiView.Application.Main (System.String[] args) [0x00000] in
/Users/Dan/Desktop/AppTouch_dev /Main.cs:18
Am I trying to deserialize the wrong thing?
Any pointers will be greatly appreciated.
I'm not entirely sure what the issue you are seeing is. It may just be that your json is badly formatted.
It it helps, the way I'd normally tackle this is to:
Use a tool like http://chris.photobooks.com/json/ to validate the JSON - in this case, this revealed to me an error - your "ResponseData" array was not terminated. The fixed code is:
{"ResponseData":[
{"ClusterID":"c02f1f5c-c61b-4f2c-ab5a-249966b3cdef","ClusterName":"Northeast",
"Courses":
[
{"CourseID":"8ab4f2b3-8160-4d7e-b79f-8d8b58926cc0","CourseName":"Home Course","SubCourses":
[{"SubCourseName":"SubCourse1","SubCourseNumber":18}]},
{"CourseID":"b3223464-333b-4c54-89c2-23908e0510c9","CourseName":"Away Course","SubCourses":
[{"SubCourseName":"SubCourse1","SubCourseNumber":19}]}
],
"IsHomeCluster":true}
]
,"ResponseErrors":[]}
copy the corrected JSON into http://json2csharp.com/
This gives me classes like:
public class SubCours
{
public string SubCourseName { get; set; }
public int SubCourseNumber { get; set; }
}
public class Cours
{
public string CourseID { get; set; }
public string CourseName { get; set; }
public List<SubCours> SubCourses { get; set; }
}
public class ResponseData
{
public string ClusterID { get; set; }
public string ClusterName { get; set; }
public List<Cours> Courses { get; set; }
public bool IsHomeCluster { get; set; }
}
public class RootObject
{
public List<ResponseData> ResponseData { get; set; }
public List<object> ResponseErrors { get; set; }
}
Use the JsonConvert.DeserializeObject<RootObject>(json) to get a deserialized RootObject
There is also a new Paste As Classes feature for JSON available: http://blogs.msdn.com/b/webdev/archive/2012/12/18/paste-json-as-classes-in-asp-net-and-web-tools-2012-2-rc.aspx

Asp.Net Web Api & JSON.Net - Deserialize body into .Net generic objects

I want to deserialize the body into .net classes (dictionary and list).
(so my code is transparent for xml and json)
public HttpResponseMessage Post([FromBody]IDictionary<string, object> body)
{
}
Currently I use a converter to handle nested dictionary deserialization.
class IDictionaryConverter : CustomCreationConverter<IDictionary<string, object>>
{
public override IDictionary<string, object> Create(Type objectType)
{
return new Dictionary<string, object>();
}
public override bool CanConvert(Type objectType)
{
// in addition to handling IDictionary<string, object>
// we want to handle the deserialization of dict value
// which is of type object
return objectType == typeof(object) || base.CanConvert(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Null)
return base.ReadJson(reader, objectType, existingValue, serializer);
// if the next token is not an object
// then fall back on standard deserializer (strings, numbers etc.)
return serializer.Deserialize(reader);
}
}
But nested JLists are not converted to List.
Maybe I can create a IListConverter, but is there an better way to do this?
I want in my web api only one code. not: if json do that, if xml do that...
This solves my problem:
class IDictionaryConverter : CustomCreationConverter<IDictionary<string, object>>
{
public override IDictionary<string, object> Create(Type objectType)
{
return new Dictionary<string, object>();
}
public override bool CanConvert(Type objectType)
{
// in addition to handling IDictionary<string, object>
// we want to handle the deserialization of dict value
// which is of type object
return objectType == typeof(object) || base.CanConvert(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Null)
return base.ReadJson(reader, objectType, existingValue, serializer);
if (reader.TokenType == JsonToken.StartArray)
return serializer.Deserialize<IList<object>>(reader);
// if the next token is not an object
// then fall back on standard deserializer (strings, numbers etc.)
return serializer.Deserialize(reader);
}
}