How to interpret and deserialize this JSON response - c#

This question originates from another topic which can be found at:
Extract objects from JSON array to list
The thing is that I'm receiving the following JSON response, and my JSON.NET deserializer doesn't understand it, but several other JSON validators like https://jsonlint.com say that it is valid.
[
{"value":"{\"code\":\"MO\",\"description\":\"Monday\",\"isSet\":false}","nr":1}
,{"value":"{\"code\":\"TU\",\"description\":\"Tuesday\",\"isSet\":true}","nr":2}
]
In my opinion the problem here is that the value object looks like a JSON object, but actually is a string.
JsonConvert.DeserializeObject keeps throwing errors until I remove the additional quotes (") and escaping chars.
So the question is, why is this response formatted like this? And how to tell the deserializer how to work with it? I'm sure that removing or replacing chars isn't the way to go.
Here is what i'm doing:
public class Value
{
public string code { get; set; }
public string description { get; set; }
public bool isSet { get; set; }
}
public class RootObject
{
public Value value { get; set; }
public int nr { get; set; }
}
var json = JsonConvert.DeserializeObject<List<RootObject>>(serviceResult);
The above doesn't work.
For the time being I have solved the issue this way. But I keep thinking that the above, with the deserializer, is more elegant.
JArray jArray = JArray.Parse(serviceResult);
List<Value> values = jArray.Select(x => JObject.Parse(x["value"].ToString()).ToObject<Value>()).ToList();

The easiest way to do this would be to use a custom JsonConverter, for example something like this:
public class StringToObjectConverter<T> : Newtonsoft.Json.JsonConverter
{
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
//This will only be needed if you also need to serlialise
writer.WriteRaw(JsonConvert.SerializeObject(value));
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
return JsonConvert.DeserializeObject<T>(reader.Value.ToString());
}
public override bool CanRead => true;
//We can only work with the type T, you could expand this to cope with derived types
public override bool CanConvert(Type objectType) => typeof(T) == objectType;
}
Now using these models, noting in particular the attribute on the Value property:
public class RootObject
{
[JsonConverter(typeof(StringToObjectConverter<Value>))]
public Value value { get; set; }
public int nr { get; set; }
}
public class Value
{
public string code { get; set; }
public string description { get; set; }
public bool isSet { get; set; }
}
Now it's a simple deserialisation:
var json = "....";
var rootObjects = JsonConvert.DeserializeObject<List<RootObject>>(json);

Related

ReadAsAsync JSON with non-static datamodels into model

The following is a model based on the response from an API (using sample data):
public class SchoolInfoModel
{
public string Name { get; set; }
public string Website { get; set; }
public Address Address { get; set; }
public List<SchoolTypeModel> SchoolTypes { get; set; }
}
The SchoolTypeModel is where I'm stuck. The SchoolType will return a list but the content of the list might contain one other model or two other models or more. Basically a list of a number of different models. But I do not know in advance which models I receive. How do I map these?
Examples of "SchoolType" models I can receive:
public class HighSchoolModel
{
public string Type { get; set; }
public string SubName { get; set; }
public bool BA { get; set; }
public bool CP { get; set; }
public bool HU { get; set; }
public bool MN { get; set; }
public bool TI { get; set; }
}
public class SpecialPurposeSchoolModel
{
public string Type { get; set; }
public string SubName { get; set; }
public bool AH { get; set; }
}
I have a total of about 10 different types of school.
Here's a way you can achieve this with JSON.Net (which means it will work in .NET Framework 4.8). You can create a custom converter to figure out what properties exist in this JSON and use that to determine which type to deserialise to. For example:
public class SchoolTypeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
=> objectType == typeof(SchoolTypeModel);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
if (obj.ContainsKey("AH"))
{
return obj.ToObject<SpecialPurposeSchoolModel>(serializer);
}
if (obj.ContainsKey("BA"))
{
return obj.ToObject<HighSchoolModel>(serializer);
}
// We have no idea what this is, so panic
throw new Exception("No idea what to do with this value!");
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
// We are only using this to read JSON
throw new NotImplementedException();
}
}
And to read the JSON:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new SchoolTypeConverter());
var result = JsonConvert.DeserializeObject<SchoolInfoModel>(yourJsonString,
settings); // Pass in the settings object we created above

Newtonsoft Json Deserialization 1 json property to decimal or int c# properties

How to achieve below deserialization. value in JSON sometime int and sometime it's decimal. I am working under multiple restrictions so -
can't change value as int property. It may break existing contract and this is use all around system.
have to use MyType as this is use all around system
I noticed decimal in JSON to int deserialization will throw exception.
public class MyType
{
[JsonProperty("type")]
[Required]
public string Type { get; set; }
[JsonProperty("value")] // existing field
public int Value { get; set; }
[JsonProperty("value")] // new field planning to add for new data
public decimal Value2 { get; set; }
}
You could leverage a custom JsonConverter. Decorate your class:
[JsonConverter(typeof(CustomConverter))]
public class MyType
{
[JsonProperty("type")]
[Required]
public string Type { get; set; }
[JsonProperty("value")] // existing field
public int Value { get; set; }
// new field planning to add for new data
public decimal Value2 { get; set; }
}
Where CustomConverter is defined (roughly):
public class CustomConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JToken.Load(reader) as JObject;
MyType myType = new MyType();
myType.Type = jObject.GetValue("type").ToString();
JToken tokenValue = jObject["value"];
if (tokenValue.Type == JTokenType.Integer)
{
myType.Value = int.Parse(tokenValue.ToString());
}
else if (tokenValue.Type == JTokenType.Float) {
myType.Value2 = decimal.Parse(tokenValue.ToString());
}
return myType;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
Note that one of Value and Value2 is implicitly set to 0 whereas the other property contains deserialized value.
To test the solution execute:
string json = #"{type:""Type1"",value: 27.99}";
MyType temp = JsonConvert.DeserializeObject<MyType>(json);

Json.NET: Converting objects to DateTimeOffsets?

I'm making requests to an API service that returns dates as JSON in the following form:
{
"dateCreated": {
"date":8,
"day":4,
"hours":22,
"minutes":44,
"month":10,
"nanos":241000000,
"seconds":46,
"time":1194590686241,
"timezoneOffset":480,
"year":107
}
}
Here time is the number of milliseconds since the Unix epoch, so that's really the only property I want to pay attention to.
Would it be possible to write a custom Json.NET converter that deserializes this to something like
public class ApiResponse
{
public ApiResponse(DateTimeOffset dateCreated)
{
DateCreated = dateCreated;
}
public DateTimeOffset DateCreated { get; }
}
(The converter would call DateTimeOffset.FromUnixTimeMilliseconds on the object's time property.)
Here is the code I have so far:
public class ApiDateConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return typeof(DateTimeOffset) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// What type is existingValue here? How do I get existingValue.time?
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
}
Option 1: Your ApiDateConverter can be completed by:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
dynamic jObject = JObject.Load(reader);
return DateTimeOffset.FromUnixTimeMilliseconds(jObject.time.Value);
}
Option 2: Without using custom converter:
public class DateSerialized
{
public int date { get; set; }
public int day { get; set; }
public int hours { get; set; }
public int minutes { get; set; }
public int month { get; set; }
public int nanos { get; set; }
public int seconds { get; set; }
public long time { get; set; }
public int timezoneOffset { get; set; }
public int year { get; set; }
}
public class ApiResponse
{
[JsonIgnore]
public DateTimeOffset DateCreated => DateTimeOffset.FromUnixTimeMilliseconds(DateSerialized.time);
[JsonProperty("dateCreated")]
private DateSerialized DateSerialized { get; set; }
}
It is certainly possible to write a Json.Net converter to translate the JSON directly into your domain object, but I don't advise this.
Instead, I would suggest you create a DTO (data transfer object) that mirrors the data:
public class DateDto
{
public long Time { get; set; }
}
Then have Json.Net deserialize into this object.
Once you have that, you can create a pure .Net converter that maps the DateDto to a DateTime or whatever structure you want (supposing maybe that you are using the NodaTime library instead of .Net's DateTime).
By doing this, you save yourself the headache of implementing custom deserialization logic, and you're following the S in SOLID: Single Responsibility Principle. You have an object whose purpose is solely to represent the data, and a converter to translate that into whatever your application uses.
It also increases the testability of your code.

Deserialize Json arrays and simple object

I´m deserializing a JSON response and there is a object that could be an array or a simple object, for example:
{
"ValueAdds":{
"#size":3,
"ValueAdd":[
{
"description":"desc 1"
},
{
"description":"desc 1"
}
]
}
}
and the other case:
{
"ValueAdds": {
"#size": "1",
"ValueAdd": {
"description": "Internet inalámbrico gratuito"
}
}
}
The entities:
public ValueAddsWrap
{
public ValueAddBase ValueAdds { set; get; }
}
public class ValueAddBase
{
public ValueAdd[] ValueAdd { set; get; }
}
public class ValueAdd
{
public string description { set; get; }
}
I´m getting an exception when I receive a simple object. How could I design the entities in order to deserialize an array and a simple object?
I´m using Newtonsoft:
T resp_object = JsonConvert.DeserializeObject<T>(json);
and the exception:
An exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll but was not handled in user code
Additional information: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'rce.bookings.business.expedia.Responses.ValueAdd[]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
You really can't do what I think you're asking. In the first case you've got a collection inside the object you're getting back.
public class ValueAdd
{
public string description { get; set; }
}
public class ValueAdds
{
public int size { get; set; }
public List<ValueAdd> ValueAdd { get; set; }
}
public class RootObject
{
public ValueAdds ValueAdds { get; set; }
}
In the second case you've got a single instance of an object.
public class ValueAdd
{
public string description { get; set; }
}
public class ValueAdds
{
public string size { get; set; }
public ValueAdd ValueAdd { get; set; }
}
public class RootObject
{
public ValueAdds ValueAdds { get; set; }
}
You might be able to get away with using a dynamic for the ValueAdd member, but that presents a whole bunch of annoying problems of its own as you'll still have to figure out whether it contains a collection or a single instance.
Realistically I see you have two options:
Option #1: Figure out if you've got the version with the collection or the single instance and deserialize into the appropriate type.
Option #2: Can you contact the author of the API and have them send back a consistent JSON structure? That's a lousy API if it changes the structure based on whether there are one or more than one ValueAdd objects.
With a JsonConverter:
public class ValueAddsConverter : 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)
{
var valueAdds = new ValueAdds();
var jo = (JObject)JObject.Load(reader)["ValueAdds"];
valueAdds.Size = Int32.Parse((string)jo["#size"]);
if (valueAdds.Size > 1)
{
valueAdds.ValueAdd = jo["ValueAdd"].Children().Select(x => x.ToObject<ValueAdd>());
}
else
{
valueAdds.ValueAdd = new List<ValueAdd>{jo["ValueAdd"].ToObject<ValueAdd>()};
}
return valueAdds;
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof (ValueAdds));
}
}
public class ValueAdd
{
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
}
[JsonConverter(typeof (ValueAddsConverter))]
public class ValueAdds
{
public int Size { get; set; }
public IEnumerable<ValueAdd> ValueAdd { get; set; }
}

how do deserialize this json string to class

I have the following json string:
{"Visits":[true,"DockedOnly","leftZone","0","500",0,0,0],
"Weather":[true,"DockedOnly","leftZone","0","0",0,0,1],
"ContactUs":[true,"DockedOnly","leftZone","0","317",0,0,2],
"Birthdays":[true,"DockedOnly","middleZone","0","0",0,0,0],
"Reminders":[true,"DockedOnly","middleZone","0","145",0,0,1],
"Messages":[true,"DockedOnly","middleZone","0","0",0,0,2],
"Availability":[true,"DockedOnly","middleZone","0","0",0,0,3],
"Settings":[false,"DockedOnly","leftzone","0","155",0,0,0]}
Is there anyway to deserialize to something like the following?
[Serializable]
public class WidgetProps
{
public bool Visible { get; set; }
public string DockState { get; set; }
public string Zone { get; set; }
public string Top { get; set; }
public string Left { get; set; }
public int UnusedA { get; set; }
public int UnusedB { get; set; }
public int Position { get; set; }
}
[Serializable]
public class WidgetLayout
{
public WidgetProps Visits { get; set; }
public WidgetProps Weather { get; set; }
public WidgetProps ContactUs { get; set; }
public WidgetProps Birthdays { get; set; }
public WidgetProps Reminders { get; set; }
public WidgetProps Messages { get; set; }
public WidgetProps Availability { get; set; }
public WidgetProps Settings { get; set; }
}
or
public class Widget
{
public string WidgetName { get; set; }
public WidgetProps props { get; set; }
}
List<Widget> MyWidgets;
I am given the json string so I can't change how it is given to me but maybe I could tinker with it after I get it so it will work.
I tried:
string s = "{\"Visits\":[true,\"DockedOnly\",\"leftZone\",\"0\",\"500\",0,0,0],\"Weather\":[true,\"DockedOnly\",\"leftZone\",\"0\",\"0\",0,0,1],\"ContactUs\":[true,\"DockedOnly\",\"leftZone\",\"0\",\"317\",0,0,2],\"Birthdays\":[true,\"DockedOnly\",\"middleZone\",\"0\",\"0\",0,0,0],\"Reminders\":[true,\"DockedOnly\",\"middleZone\",\"0\",\"145\",0,0,1],\"Messages\":[true,\"DockedOnly\",\"middleZone\",\"0\",\"0\",0,0,2],\"Availability\":[true,\"DockedOnly\",\"middleZone\",\"0\",\"0\",0,0,3],\"Settings\":[false,\"DockedOnly\",\"leftzone\",\"0\",\"155\",0,0,0]}}";
var sd = new JavaScriptSerializer().Deserialize < List<Widget>>(s);
and
var sd = new JavaScriptSerializer().Deserialize < WidgetLayout >(s);
This isn't working because you're trying to deserialize an array into an object. The json.NET deserializer will not be able make that conversion.
Because your json arrays have multiple types in them you'll have to deserialize into the smallest common denominator, in this case, object. From there I would recommend writing a method to assign each index to it's corresponding property in WidgetProps.
So basically, define this constructor;
public WidgetProps(object[] props)
{
Visible = (bool)props[0];
DockState = (string)props[1];
// ext
}
I would have something like a WidgetDirty class that I do the initial deserilization into. From there you can create a new instance of WidgetLayout by instantiating each of it's properties like myWidgetLayoutInstance.Visits = new WidgetProp(myWidgetDirtyInstance.Visits); I'd probably hide this mess in a WidgetLayout constructor that takes a WidgetDirty as it's only arg.
yes it's disgusting... but I don't know of any real alternatives because that json's design just isn't very compatible with the C# language. If you're strongly apposed to this I might look at the dynamic type. I haven't used it in C# and probably never will, but I know that deserializing that in a dynamic language like PHP would be no trouble at all.
This doesn't work, because array is normally not deserialized as an object. If possible, I think you should fix the JSON. If you can't do that, and you're using JSON.NET, you can create JsonConverter for WidgetProps that manually converts the array to the object:
class WidgetPropsConverter : JsonConverter
{
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
public override object ReadJson(
JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var array = serializer.Deserialize<object[]>(reader);
return new WidgetProps
{
Visible = (bool)array[0],
DockState = (string)array[1],
Zone = (string)array[2],
Top = (string)array[3],
Left = (string)array[4],
Position = (int)(long)array[7]
};
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(WidgetProps);
}
}
You would then use it like this:
var result = JsonConvert.DeserializeObject<WidgetLayout>(
jsonString, new WidgetPropsConverter());

Categories