Deserializing a JSON array with awkward additional property in the schema - c#

I have a very similar issue to this question here, except my application is in C#, and I can't figure out how to convert the solution unfortunately. I am trying to deserialize a JSON result that looks like this:
"error":[],
"result":
{
"MANAEUR":[
[1619042400,"1.11200","1.13488","1.08341","1.10077","1.09896","58878.56534370",137],
[1619046000,"1.09767","1.12276","1.08490","1.11097","1.10456","25343.25910419",77],
],
"last":1619118000
}
I use the following classes:
public class ResponseBase
{
[JsonProperty(PropertyName = "error")]
public List<string> Error;
}
public class OHLCResponse : ResponseBase
{
[JsonProperty("result")]
public OHLCResult Result;
}
public class OHLCResult
{
[JsonProperty("pair_names")]
public Dictionary<string, OHLC[]> GetHistory;
[JsonProperty("last")]
public long Last;
}
.... and then finally the guts of it:
public class OHLC
{
public int Time;
public decimal Open;
public decimal High;
public decimal Low;
public decimal Close;
public decimal Vwap;
public decimal Volume;
public int Count;
}
I have a standard deserializer class which works for all other calls I am using to the same API, but I cannot get this call to work. When I retrieve OHLCResponse object,I don't get an error, and "Result.Last" is always populated, but the expected array of OHLC items in "Result.GetHistory" is always empty/null. I know that the data has been returned successfully since I can see the data in the variable returned from the WebRequest that I am then passing to the deserializer function, so it must be that I have these classes laid out incorrectly I guess.
Can anyone see what I'm doing wrong?
Many thanks in advance, Dave

The object you posted isn't valid JSON. The outside curly braces are missing. So I am going to assume it should look like this:
{
"error": [],
"result": {
"MANAEUR": [
[1619042400, "1.11200", "1.13488", "1.08341", "1.10077", "1.09896", "58878.56534370", 137],
[1619046000, "1.09767", "1.12276", "1.08490", "1.11097", "1.10456", "25343.25910419", 77],
],
"last": 1619118000
}
}
Anonymous Deserialization
The first method you could do, which may be a tad kludgy since you have to deserialize twice, is use anonymous deserialization.
Let's start by defining some models:
public sealed class OHLCModel
{
public long Time { get; set; }
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
public decimal Vwap { get; set; }
public decimal Volume { get; set; }
public int Count { get; set; }
}
public sealed class ResultModel
{
[JsonIgnore]
public IEnumerable<OHLCModel> Manaeur { get; set; }
[JsonProperty("last")]
public long Last { get; set; }
}
public sealed class RootModel
{
[JsonProperty("error")]
public List<string> Error { get; set; }
[JsonProperty("result")]
public ResultModel Result { get; set; }
}
As you can see we are ignoring the Manaeur object when serialization happens.
To make this method work, we'd do this:
var json = System.IO.File.ReadAllText(#"c:\users\andy\desktop\test.json");
// First, just grab the object that has the mixed arrays.
// This creates a "template" of the format of the target object
var dto = JsonConvert.DeserializeAnonymousType(json, new
{
Result = new
{
Manaeur = new List<List<object>>()
}
});
// Next, deserialize the rest of it
var fullObject = JsonConvert.DeserializeObject<RootModel>(json);
// transfer the DTO using a Select statement
fullObject.Result.Manaeur = dto.Result.Manaeur.Select(x => new OHLCModel
{
Time = Convert.ToInt64(x[0]),
Open = Convert.ToDecimal(x[1]),
High = Convert.ToDecimal(x[2]),
Low = Convert.ToDecimal(x[3]),
Close = Convert.ToDecimal(x[4]),
Vwap = Convert.ToDecimal(x[5]),
Volume = Convert.ToDecimal(x[6]),
Count = Convert.ToInt32(x[7])
});
This isn't the most ideal solution as you are tightly coupling to the model in a few spots. The ideal way to do this would be to make a custom JsonSerializer.
Use a Custom JsonConverter
First thing we do is change your ResultModel to look like this:
public sealed class ResultModel
{
[JsonConverter(typeof(ManaeurJsonConverter)), JsonProperty("MANAEUR")]
public IEnumerable<OHLCModel> Manaeur { get; set; }
[JsonProperty("last")]
public long Last { get; set; }
}
Then implement a JsonConverter:
public sealed class ManaeurJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => false; // this will never get called
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var lst = JArray.Load(reader).ToObject<List<List<object>>>();
return lst.Select(x => new OHLCModel
{
Time = Convert.ToInt64(x[0]),
Open = Convert.ToDecimal(x[1]),
High = Convert.ToDecimal(x[2]),
Low = Convert.ToDecimal(x[3]),
Close = Convert.ToDecimal(x[4]),
Vwap = Convert.ToDecimal(x[5]),
Volume = Convert.ToDecimal(x[6]),
Count = Convert.ToInt32(x[7])
});
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{ // we don't need to write
throw new NotImplementedException();
}
}
You can then simply call it as so:
var json = System.IO.File.ReadAllText(#"c:\users\andy\desktop\test.json");
var fullObject = JsonConvert.DeserializeObject<RootModel>(json);

Your JSON is not valid, I had to modify it to make it valid JSON (https://jsonformatter.org/). I added the root brackets and removed the comma delimter after the second inner array entry.
Valid JSON:
{
"error":[],
"result":
{
"MANAEUR":[
[1619042400,"1.11200","1.13488","1.08341","1.10077","1.09896","58878.56534370",137],
[1619046000,"1.09767","1.12276","1.08490","1.11097","1.10456","25343.25910419",77]
],
"last":1619118000
}
}
After updating the JSON, I used Visual Studio's 'Paste Special' to generate C# objects from the JSON. The following classes were created.
public class RootObject
{
[JsonProperty("error")]
public object[] Error { get; set; }
[JsonProperty("result")]
public Result Result { get; set; }
}
public class Result
{
[JsonProperty("MANAEUR")]
public object[][] Manaeur { get; set; }
[JsonProperty("last")]
public int Last { get; set; }
}
With the above JSON and classes, I used the following to deserialize the JSON.
string json = "{\"error\":[],\"result\":{\"MANAEUR\":[[1619042400,\"1.11200\",\"1.13488\",\"1.08341\",\"1.10077\",\"1.09896\",\"58878.56534370\",137],[1619046000,\"1.09767\",\"1.12276\",\"1.08490\",\"1.11097\",\"1.10456\",\"25343.25910419\",77]],\"last\":1619118000}}";
var obj = JsonConvert.DeserializeObject<RootObject>(json);
EDIT:
To handle the MANAEUR property where the key label can be different.
Create a JsonConverter...
public class ManaeurConverter : JsonConverter
{
private Dictionary<string, string> propertyMappings { get; set; }
public ManaeurConverter()
{
this.propertyMappings = new Dictionary<string, string>
{
{"NOTMANAEUR","MANAEUR"}
};
}
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)
{
object instance = Activator.CreateInstance(objectType);
var props = objectType.GetTypeInfo().DeclaredProperties.ToList();
JObject jo = JObject.Load(reader);
foreach (JProperty jp in jo.Properties())
{
if (!propertyMappings.TryGetValue(jp.Name, out var name))
name = jp.Name;
PropertyInfo prop = props.FirstOrDefault(pi =>
pi.CanWrite && pi.GetCustomAttribute<JsonPropertyAttribute>().PropertyName == name);
prop?.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
}
return instance;
}
public override bool CanConvert(Type objectType)
{
return objectType.GetTypeInfo().IsClass;
}
public override bool CanWrite => false;
}
... Add the JsonConverter attribute to the class...
[JsonConverter(typeof(ManaeurConverter))]
public class Result
{
[JsonProperty("MANAEUR")]
public object[][] Manaeur { get; set; }
[JsonProperty("last")]
public int Last { get; set; }
}
... and parse like so...
string json_Manaeur = "{\"error\":[],\"result\":{\"MANAEUR\":[[1619042400,\"1.11200\",\"1.13488\",\"1.08341\",\"1.10077\",\"1.09896\",\"58878.56534370\",137],[1619046000,\"1.09767\",\"1.12276\",\"1.08490\",\"1.11097\",\"1.10456\",\"25343.25910419\",77]],\"last\":1619118000}}";
string json_Not_Manaeur = "{\"error\":[],\"result\":{\"NOTMANAEUR\":[[1619042400,\"1.11200\",\"1.13488\",\"1.08341\",\"1.10077\",\"1.09896\",\"58878.56534370\",137],[1619046000,\"1.09767\",\"1.12276\",\"1.08490\",\"1.11097\",\"1.10456\",\"25343.25910419\",77]],\"last\":1619118000}}";
var objManaeur = JsonConvert.DeserializeObject<RootObject>(json_Manaeur);
var objNotManaeur = JsonConvert.DeserializeObject<RootObject>(json_Not_Manaeur);

Related

JSON deserialization error when property name is missing

I am trying to consume an external web service and I am using .NET Core and the Flurl framework. I get a response from the service like below:
[
"Successful Request: 96 Results",
[
{
"eventdate":"2019-10-18",
"name":"",
"url":"",
"info":"",
"showtime":null,
"url_tix":"",
"event_owner":"xxx",
"follow_url":"xxx",
"event_image":"xxx",
"venue":"xxx",
"city":"xxx",
"country":"xxx",
"state":""
}
]
]
and I have a C# entity definition like below:
public class ServiceResponce
{
public Event[] Events { get; set; }
}
public class Event
{
[JsonProperty("eventdate")]
public DateTimeOffset Eventdate { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("showtime")]
public object Showtime { get; set; }
[JsonProperty("url_tix")]
public object UrlTix { get; set; }
[JsonProperty("event_owner")]
public string EventOwner { get; set; }
[JsonProperty("follow_url")]
public Uri FollowUrl { get; set; }
[JsonProperty("event_image")]
public object EventImage { get; set; }
[JsonProperty("venue")]
public string Venue { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("state")]
public string State { get; set; }
}
When I tried to call the Flurl method to consume the web service like below:
var result = await serviceUrl.GetJsonAsync<ServiceResponce>();
I got the error mentioned below:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'xxx.ServiceResponce' because the type requires a JSON object (e.g.
{"name":"value"}) to deserialize correctly. To fix this error either
change the JSON to a JSON object (e.g. {"name":"value"}) or change the
deserialized type to an array or a type that implements a collection
interface (e.g. ICollection, IList) like List that can be
deserialized from a JSON array. JsonArrayAttribute can also be added
to the type to force it to deserialize from a JSON array. Path '',
line 1, position 1.
Do you have any solution for that? Any help always is welcome.
The problem here is the JSON response is actually an array of mixed types. The first element of the array is a string, and the second element is an array of event objects. You will need a custom JsonConverter to deserialize this JSON.
Here is the code you would need for the converter:
class ServiceResponceConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(ServiceResponce));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray ja = JArray.Load(reader);
ServiceResponce resp = new ServiceResponce();
resp.Events = ja[1].ToObject<Event[]>(serializer);
return resp;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, add a [JsonConverter] attribute to the ServiceResponce class to tie it to the converter:
[JsonConverter(typeof(ServiceResponceConverter))]
public class ServiceResponce
{
public Event[] Events { get; set; }
}
Now you can deserialize to the ServiceResponce class as normal and it will work properly.
Optional: If you also want to capture the "Successful Request: 96 Results" string from the response, add
public string ResultString { get; set; }
to the ServiceResponce class and add the following line to the the ReadJson method of the converter:
resp.ResultString = (string)ja[0];
Working demo here: https://dotnetfiddle.net/opPUmX
I think the problem is in the Json object, I have generated a class with 'Newtonsoft.Json', if you can try this code:
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using MyNameSpace;
//
// var event = Event.FromJson(jsonString);
namespace MyNameSpace
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class EventClass
{
[JsonProperty("eventdate")]
public DateTimeOffset Eventdate { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("showtime")]
public object Showtime { get; set; }
[JsonProperty("url_tix")]
public string UrlTix { get; set; }
[JsonProperty("event_owner")]
public string EventOwner { get; set; }
[JsonProperty("follow_url")]
public string FollowUrl { get; set; }
[JsonProperty("event_image")]
public string EventImage { get; set; }
[JsonProperty("venue")]
public string Venue { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("state")]
public string State { get; set; }
}
public partial struct EventUnion
{
public EventClass[] EventClassArray;
public string String;
public static implicit operator EventUnion(EventClass[] EventClassArray) => new EventUnion { EventClassArray = EventClassArray };
public static implicit operator EventUnion(string String) => new EventUnion { String = String };
}
public class Event
{
public static EventUnion[] FromJson(string json) => JsonConvert.DeserializeObject<EventUnion[]>(json, MyNameSpace.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this EventUnion[] self) => JsonConvert.SerializeObject(self, MyNameSpace.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
EventUnionConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class EventUnionConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(EventUnion) || t == typeof(EventUnion?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new EventUnion { String = stringValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<EventClass[]>(reader);
return new EventUnion { EventClassArray = arrayValue };
}
throw new Exception("Cannot unmarshal type EventUnion");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (EventUnion)untypedValue;
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
if (value.EventClassArray != null)
{
serializer.Serialize(writer, value.EventClassArray);
return;
}
throw new Exception("Cannot marshal type EventUnion");
}
public static readonly EventUnionConverter Singleton = new EventUnionConverter();
}
}

Deserialize json string to c# object using newtonsoft

I have been working with the project, where i have to make external RESTful service call to get some data.
The problem i'm facing here is, the response i'm getting from the service is different on different scenario. For example.
On one scenario, i'm getting below response
{
"id":3000056,
"posted_date":"2016-04-15T07:16:47+00:00",
"current_status":"initialized",
"customer":{
"name" : "George",
"lastName" : "Mike"
},
"application_address":{
"addressLine1" : "Lin1",
"addressLine2" : "Lin2",
}
}
In the other scenario, im getting below response
{
"id":3000057,
"posted_date":"2016-04-15T07:16:47+00:00",
"current_status":"initialized",
"customer":[],
"application_address":[]
}
The problem here is, i have below model, and i'm deserializing it by newtonsoft deserailization.
public class Response
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("posted_date")]
public DateTime PostedDate { get; set; }
[JsonProperty("current_status")]
public string CurrentStatus { get; set; }
[JsonProperty("customer")]
public Customer Customer { get; set; }
[JsonProperty("application_address")]
public ApplicationAddress ApplicationAddress { get; set; }
}
public Class Customer
{
public string name { get; set; }
public string lastName { get; set; }
}
public classs ApplicationAddress
{
public string addreesLine1{ get; set; }
public string addreesLine1{ get; set; }
}
For the first response, it will desrialize. But for the second response, the response is not getting deserialized as the response contains [] for Customer and ApplicationAddrees object. While deserializing, it is treating as a array, but actually it is not.
Note : Below code i'm using for Deserializing.
Response response = JsonConvert.DeserializeObject(result);
Is there any configuration we can do before serializing? Is newtonsoft facilitate that feature?
Thanks .
If you are sure that there will be no arrays in this properties, than you can consider using JsonConverter like this:
public class FakeArrayToNullConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return false;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return null;
}
return token.ToObject<T>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then put additional attribure to your model:
[JsonProperty("customer")]
[JsonConverter(typeof(FakeArrayToNullConverter<Customer>))]
public Customer Customers { get; set; }
[JsonProperty("application_address")]
[JsonConverter(typeof(FakeArrayToNullConverter<ApplicationAddress>))]
public ApplicationAddress ApplicationAddressList { get; set; }
And when in your JSON string for this properties it will be an array [], you will simply deserialize it with null object.
You can't instruct deserialize to handle "[]" as something different as it represents an array (are you sure you never will get customers and addresses in those arrays??)
So you could deserialize to an anonymous type and then map it to your structure.
This is just a guess but can you check if this works:
public class ApplicationAddress
{
private readonly string[] _array = new string[2];
public string this[int index]
{
get { return _array[index]; }
set { _array[index] = value; }
}
public string addreesLine1
{
get { return this[0]; }
set { this[0] = value; }
}
public string addreesLine2
{
get { return this[1]; }
set { this[1] = value; }
}
}

How can I use Json.net to populate a model with custom bindings/mappings?

Here's my JSON:
{
"macAddress": "000A959D6816",
"softwareVersion": "1.2.1.5-UnnecessaryInfo",
"lastUpdated": "2015-04-03 20:46:40.375 -0500",
"charging": true
}
And using Json.NET, I can do the following in C#:
namespace JsonTest
{
public class Tablet
{
public string MacAddress { get; set; }
public string SoftwareVersion { get; set; }
public DateTime LastUpdated { get; set; }
public bool Charging { get; set; }
}
public class TestClass
{
public void Test()
{
var json = "{ ... }"; // filled in with JSON info from above
var model = new Tablet();
try
{
JsonConvert.PopulateObject(json, model);
}
catch (JsonSerializationException ex)
{
Console.WriteLine(ex);
}
}
}
}
So far, so good. The code I have here works great. It populates my model object with all the data from the Json. However, I don't really want the SoftwareVersion property of my model to be a string; I'd rather have it be an instance of the System.Version class. In other words, I'd like my Tablet class to look more like this:
public class Tablet
{
public string MacAddress { get; set; }
public Version SoftwareVersion { get; set; }
public DateTime LastUpdated { get; set; }
public bool Charging { get; set; }
}
I don't care about the unnecessary info that gets appended on the end of that version string, so I'd like to write some sort of mapper/binder class that examines the version string from the Json, strips off the unnecessary info, and then parses that field into a Version object before proceeding to populate my model. I do know how to write that individual parsing method; this would do the trick:
private static Version ParseVersion(object versionObj)
{
var pattern = new Regex(#"^[\d.]+");
var versionString = versionObj.ToString();
if (!pattern.IsMatch(versionString)) return null;
var match = pattern.Match(versionString);
versionString = match.Groups[0].ToString();
Version version;
Version.TryParse(versionString, out version);
return version;
}
What I don't know is where and how to "plug this in" during the JsonConvert process. I see that the PopulateObject takes an optional JsonSerializerSettings parameter, and in turn that has several different object initializer parameters like Binder and Converters. But I'm not sure which one to use, nor how to write either of those classes to do what I'm describing here. How do I do it? And what is the difference between a Binder and a Converter?
Just add an appropriate JsonConverterAttribute to your Version property, and PopulateObject will use it:
public class Tablet
{
public string MacAddress { get; set; }
[JsonConverter(typeof(VersionConverter))]
public Version SoftwareVersion { get; set; }
public DateTime LastUpdated { get; set; }
public bool Charging { get; set; }
}
And here is the actual converter:
public class VersionConverter : JsonConverter
{
private static Version ParseVersion(object versionObj)
{
var pattern = new Regex(#"^[\d.]+");
var versionString = versionObj.ToString();
if (!pattern.IsMatch(versionString))
return null;
var match = pattern.Match(versionString);
versionString = match.Groups[0].ToString();
Version version;
Version.TryParse(versionString, out version);
return version;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(System.Version);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
return ParseVersion((string)token);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var version = (Version)value;
if (version != null)
writer.WriteValue(value.ToString());
}
}
You should now be all set.
Alternatively, if you have a Version property appearing in many different container classes in a complex object graph, and you don't want to set the JsonConverterAttribute everywhere, you can add your converter to JsonSerializerSettings.Converters, then pass that to PopulateObject:
JsonConvert.PopulateObject(json, model, new JsonSerializerSettings { Converters = new JsonConverter [] { new VersionConverter() } } );

De-Serialize JSON to different elements based on content of element

I am trying to parse into different items a JSON file that has items with the following example content:
{
"PM00000001": { "description": "Manufacturing","cost": -1,"group":"Manufacturing","WeldAngleDegrees": 60},
"PM00000010": {"description": "Plate Roll","cost": 90,"unit": "hr","group": "Roll","setup": 0.5,"speed": 0.4},
"PM00000011": {"description": "Weld SAW","cost": 90,"unit": "hr","group": "Weld","width": 0.5,"thickness": 50}
}
Each item has a description, cost and group. The rest of the attributes depend on the group. In the example above Manufacturing has "WeldAngleDegrees", Roll has setup and speed, and Weld has width and thickness.
I am trying to use JSON.NET to parse this file.
Right now I am doing this:
string text = System.IO.File.ReadAllText(ofd.FileName);
Dictionary<string, Item> deserializedProduct = JsonConvert.DeserializeObject<Dictionary<string, Item>>(text, new ItemConverter());
with
public class Item
{
public string description { get; set; }
public double cost { get; set; }
public string group { get; set; }
}
public class ManufacturingItem : Item
{
public string WeldAngleDegrees { get; set; }
}
public class ItemConverter : CustomCreationConverter<Item>
{
public override Item Create(Type objectType)
{
return new ManufacturingItem();
}
}
Is there a way in ItemConverter to figure out which "group" the item belongs to to create the correct item type?
Is there an easier way to do this?
Instead of deriving your ItemConverter from CustomCreationConverter<T>, derive it from JsonConverter; then you will have access to the JSON via the reader. You can load the object data into a JObject, then inspect the group property to determine which class to create. Here is how the code might look:
public class ItemConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Item);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
string group = (string)jo["group"];
if (group == "Manufacturing")
{
return jo.ToObject<ManufacturingItem>();
}
else if (group == "Roll")
{
return jo.ToObject<RollItem>();
}
else if (group == "Weld")
{
return jo.ToObject<WeldItem>();
}
throw new JsonSerializationException("Unexpected item (group) type");
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Fiddle: https://dotnetfiddle.net/8ZIubu
Just deserialize your json to a dictionary, and interpret the values according to the value of group.
var dict = JsonConvert.DeserializeObject<Dictionary<string, MyItem>>(json);
public class MyItem
{
public string Description { get; set; }
public int Cost { get; set; }
public string Group { get; set; }
public int WeldAngleDegrees { get; set; }
public string Unit { get; set; }
public double Width { get; set; }
public int Thickness { get; set; }
public double Speed { get; set; }
public double Setup { get; set; }
}

Deserialize JSON String Nested Dictionary

I am trying to use Riot games REST API to make a webapp in C#. I am fine with making the requests using RESTSharp but am having some problems using JSON.Net to convert the returned Json to an object. My request returns a JSON string for example:
{\"dyrus\":{\"id\":4136713,\"name\":\"Dyrus\",\"profileIconId\":23,\"summonerLevel\":1,\"revisionDate\":1376908220000}}
I want to deserialize this into an object that has attributes: id, name, profileIconID, summonerLevel and revisionDate.
The problem I am having is that the information is being deserialized as a string because the Dictionary is nested. What is the best way to just retrieve the nested Dictionary portion of the string: {\"id\":4136713,\"name\":\"Dyrus\",\"profileIconId\":23,\"summonerLevel\":1,\"revisionDate\":1376908220000} and convert it into an object?
Thanks for your help!
Edit:
Here is what I have tried:
public class LeagueUser
{
public LeagueUser(string json)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
string jsonString = (string)serializer.DeserializeObject(json);
LeagueUser test = (LeagueUser)serializer.DeserializeObject(jsonString);
}
public int id { get; set; }
public string name { get; set; }
public long revisionDate { get; set; }
}
You don't need the constructor, change LeagueUser class to this
public class LeagueUser
{
public int id { get; set; }
public string name { get; set; }
public long revisionDate { get; set; }
}
and use Json.NET to deserialize the json into a Dictionary<string, LeagueUser>
string jsonStr = "{\"dyrus\":{\"id\":4136713,\"name\":\"Dyrus\",\"profileIconId\":23,\"summonerLevel\":1,\"revisionDate\":1376908220000}}";
var deserializedObject = JsonConvert.DeserializeObject<Dictionary<string, LeagueUser>>(jsonStr);
You can get the LeagueUser object this way
LeagueUser leagueUser = deserializedObject["dyrus"];
You can achieve what you want by creating custom converter for your LeagueUser class:
public class LeagueUserConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(LeagueUser) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (!CanConvert(objectType)) return null;
var jObject = JObject.Load(reader);
var user = new LeagueUser
{
Id = Convert.ToInt64(jObject["dyrus"]["id"]),
Name = jObject["dyrus"]["name"].ToString(),
ProfileIconId = Convert.ToInt32(jObject["dyrus"]["profileIconId"]),
SummonerLevel = Convert.ToInt32(jObject["dyrus"]["summonerLevel"]),
RevisionDate = Convert.ToInt64(jObject["dyrus"]["revisionDate"])
};
return user;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Next you need to decorate your class with the defined converter:
[JsonConverter(typeof(LeagueUserConverter))]
public class LeagueUser
{
public long Id { get; set; }
public string Name { get; set; }
public int ProfileIconId { get; set; }
public int SummonerLevel { get; set; }
public long RevisionDate { get; set; }
}
And wherever you need call DeserializeObject method:
var user = JsonConvert.DeserializeObject<LeagueUser>(json);
where the json variable is the json string you posted in your question.

Categories

Resources