Deserialize Json Object for a C # polymorphic object without typeNameHandling

My problem is that I want to deserialize the json object for a C # object, but the trick is that the C # object contains a List <abstract class>, and this abstract class is a superclass of 10 more classes.

public sealed class SearchAPIResult { public string Status; public SearchAPIQuery Query; public SearchResults Result; public SearchAPIResult() { } public SearchAPIResult(string status) { Status = status; } } 

and SearchAPIResult :

 public sealed class SearchResults { public string TextAnswer; public List<APIResultWidget> Items; public SearchResults() { Items = new List<APIResultWidget>(); } } 

here, the APIResultWidget object is an abstract class, which inherits about 10 classes.

The problem is that the JSON object does not have something automatic (e.g. typeNameHandling in JSON.NET) to direct the deserializer to which the object of the 10 derived classes belongs. instead, objects are marked with two fields: Type and SubType ... like the following

 { "Status": "OK", "Query": { "Query": "this is a query", "QueryLanguage": "EN" }, "Result": { "TextAnswer": "This is your text answer", "Items": [{ "Type": "list", "SubType": null, "Title": null, "Items": [] }, { "Type": "text", "Content": "this is some content" } ] } } 

in the previous json object, the result list contains two objects, one:

 { "Type": "list", "SubType": null, "Title": null, "Items": [] } 

which maps to a class of type listWidget (which inherits from the abstract APIResultWidget and two:

 { "Type": "text", "Content": "this is some content" } 

which maps to the textWidget class, which also inherits from the same abstract class

when i use the json.net method

 SearchAPIResult converted = (SearchAPIResult)JsonConvert.DeserializeObject(json, typeof(SearchAPIResult), new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); 

it throws the following exception:

Failed to create an instance of type Kngine.API.APIResultWidget. A type is an interface or abstract class and cannot be created. Path 'Result.Items [0] .Type', line 1, position 136.

I assume that there is a special way to indicate that this type is defined by both Type and SubType fields and provides the converter with this custom type annotator, is this true?

+3
json polymorphism c # servicestack
May 13 '13 at 10:29
source share
3 answers

Actually, the solution that I put in is the simplest solution, I extended the JavaScriptConverter class from System.Web.Extentions, I applied the deseriealize method, which automatically gets every small json object in the source object as a dictionary, and I can just fill in the fields according to the object itself, this is a bit of a manual way, but it was the only solution that I could come up with and work. Custom class implantation is as follows:

 class myCustomResolver : JavaScriptConverter { public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { if (type == typeof(APIResultWidget)) { switch ((string)dictionary["Type"]) { case "weather": { WeatherWidget x = new WeatherWidget(); x.Location = (string)dictionary["Location"]; x.Current = (CurrentWeather)dictionary["Current"]; //x.Forcast = (List<WeatherForcastItem>)dictionary["Forcast"]; System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Forcast"]); foreach (var item in itemss) { x.Forcast.Add(serializer.ConvertToType<WeatherForcastItem>(item)); } return x; }; case "text": { TextWidget x = new TextWidget(); x.Content = (string)dictionary["Content"]; return x; }; case "keyValueText": { KeyValueTextWidget x = new KeyValueTextWidget(); x.Key = (string)dictionary["Key"]; x.Key = (string)dictionary["Value"]; x.Key = (string)dictionary["ValueURL"]; return x; }; case "keyValuesText": { KeyValuesTextWidget x = new KeyValuesTextWidget(); x.Key = (string)dictionary["Key"]; //x.Values = (List<ValueItem>)dictionary["ValueItem"]; System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["ValueItem"]); foreach (var item in itemss) { x.Values.Add(serializer.ConvertToType<ValueItem>(item)); } return x; }; case "url": { URLWidget x = new URLWidget(); x.ThumbnailImageURL = (string)dictionary["ThumbnailImageURL"]; x.Title = (string)dictionary["Title"]; x.URL = (string)dictionary["URL"]; x.HTMLContent = (string)dictionary["HTMLContent"]; return x; }; case "map": { MapWidget x = new MapWidget(); System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Pins"]); foreach (var item in itemss) { x.Pins.Add(serializer.ConvertToType<MapPoint>(item)); } //x.Pins = (List<MapPoint>)dictionary["Pins"]; return x; }; case "image": { ImageWidget x = new ImageWidget(); x.Title = (string)dictionary["Title"]; x.ImageURL = (string)dictionary["ImageURL"]; x.ThumbnailURL = (string)dictionary["ThumbnailURL"]; x.PageURL = (string)dictionary["PageURL"]; return x; }; case "html": { HTMLWidget x = new HTMLWidget(); x.Title = (string)dictionary["Title"]; x.HTML = (string)dictionary["HTML"]; return x; }; case "entity": { EntityWidget x = new EntityWidget(); x.SubType = (string)dictionary["SubType"]; x.Title = (string)dictionary["Title"]; x.Abstract = (string)dictionary["Abstract"]; x.ImageURL = (string)dictionary["ImageURL"]; x.Url = (string)dictionary["Url"]; return x; }; case "chart": { ChartWidget x = new ChartWidget(); x.Title = (string)dictionary["Title"]; //x.Categories = (List<string>)dictionary["Categories"]; System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Categories"]); foreach (var item in itemss) { x.Categories.Add(serializer.ConvertToType<string>(item)); } System.Collections.ArrayList itemss2 = ((System.Collections.ArrayList)dictionary["Data"]); foreach (var item in itemss2) { x.Data.Add(serializer.ConvertToType<ChartsData>(item)); } //x.Data = (List<ChartsData>)dictionary["Data"]; return x; }; case "businessEntity": { BusinessEntityWidget x = new BusinessEntityWidget(); x.SubType = (string)dictionary["SubType"]; x.Title = (string)dictionary["Title"]; x.Abstract = (string)dictionary["Abstract"]; x.ImageURL = (string)dictionary["ImageURL"]; x.URL = (string)dictionary["URL"]; //x.Attributes = (List<KeyValueTextWidget>)dictionary["Attributes"]; System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Attributes"]); foreach (var item in itemss) { x.Attributes.Add(serializer.ConvertToType<KeyValueTextWidget>(item)); } x.Address = (string)dictionary["Address"]; x.Phone = (string)dictionary["Phone"]; x.Lat = (double)dictionary["Lat"]; x.Lng = (double)dictionary["Lng"]; System.Collections.ArrayList itemss2 = ((System.Collections.ArrayList)dictionary["OtherURLs"]); foreach (var item in itemss2) { x.OtherURLs.Add(serializer.ConvertToType<URLWidget>(item)); } //x.OtherURLs = (List<URLWidget>)dictionary["OtherURLs"]; return x; }; case "list": { switch ((string)dictionary["SubType"]) { case null: { ListWidget x = new ListWidget(); x.Title = (string)dictionary["Title"]; System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); foreach (var item in itemss) { x.Items.Add(serializer.ConvertToType<APIResultWidget>(item)); } return x; }; case "videos": { ListOfVideosWidget x = new ListOfVideosWidget(); System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); foreach (var item in itemss) { x.Items.Add(serializer.ConvertToType<URLWidget>(item)); } return x; }; case "images": { ListOfImagesWidget x = new ListOfImagesWidget(); System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); foreach (var item in itemss) { x.Items.Add(serializer.ConvertToType<ImageWidget>(item)); } return x; }; case "webResults": { ListOfWebsitesWidget x = new ListOfWebsitesWidget(); System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); foreach (var item in itemss) { x.Items.Add(serializer.ConvertToType<URLWidget>(item)); } return x; }; case "businesses": { ListOfBusinessesWidget x = new ListOfBusinessesWidget(); x.Title = (string)dictionary["Title"]; System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); foreach (var item in itemss) { x.Items.Add(serializer.ConvertToType<BusinessEntityWidget>(item)); } return x; }; } }; break; } } else //in case of objects not inheriting from the abstract class, in this case we identify each one by something else, not "type" { if (dictionary.ContainsKey("Day")) //WeatherForcastItem { WeatherForcastItem x = new WeatherForcastItem(); x.Day = (string)dictionary["Day"]; x.Hi = (string)dictionary["Hi"]; x.Lo = (string)dictionary["Lo"]; x.Status = (string)dictionary["Status"]; x.IconURL = (string)dictionary["IconURL"]; return x; } else if (dictionary.ContainsKey("Temprature")) // CurrentWeather { CurrentWeather x = new CurrentWeather(); x.Temprature = (string)dictionary["Temprature"]; x.Status = (string)dictionary["Status"]; x.WindSpeed = (string)dictionary["WindSpeed"]; x.WindDirection = (string)dictionary["WindDirection"]; x.Humidity = (string)dictionary["Humidity"]; x.IconURL = (string)dictionary["IconURL"]; x.IsNight = (string)dictionary["IsNight"]; return x; } else if (dictionary.ContainsKey("Lat")) //MapPoint { MapPoint x = new MapPoint(); x.Title = (string)dictionary["Title"]; x.Lat = (double)dictionary["Lat"]; x.Lng = (double)dictionary["Lng"]; return x; } else if (dictionary.ContainsKey("Value")) //ValueItem { ValueItem x = new ValueItem(); x.Value = (string)dictionary["Value"]; x.ValueURL = (string)dictionary["ValueURL"]; return x; } else if (dictionary.ContainsKey("name")) //ChartsData { ChartsData x = new ChartsData(); x.name = (string)dictionary["name"]; System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["name"]); foreach (var item in itemss) { x.values.Add(serializer.ConvertToType<string>(item)); } return x; } } return null; } public override IDictionary<string, object> Serialize( object obj, JavaScriptSerializer serializer) { return null; } private static readonly Type[] _supportedTypes = new[] { typeof( APIResultWidget ) }; public override IEnumerable<Type> SupportedTypes { get { return _supportedTypes; } } } 

which should match every json object with its right class, using a deserializer is pretty simple:

 var serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new[] { new myCustomResolver() }); var dataObj = serializer.Deserialize<SearchAPIResult>(response); 

this solved the deserialization problem that I had with abstract classes because it worked completely on how classes are related to each other in the first place. I don’t know if this was the most correct decision or not, but at least he solved my logo

+2
May 15 '13 at 9:10
source share

First of all, this is the best way to deserialize objects:

 var converted = JsonConvert.DeserializeObject<SearchAPIResult>(json); 

In any case, I think your problem is that you are trying to instruct the serializer to deserialize the Item property using the APIResultWidget list only. Can you update your post with this class? It would be easier for us to help you.

+1
May 13 '13 at 14:30
source share

By default, ServiceStack only serializes public properties . To get it for serializing public fields, you need to configure it with:

 JsConfig.IncludePublicFields = true; 
0
May 13 '13 at 15:33
source share



All Articles