Deserialize Mixpanel Data API Event JSON C# - c#

I'm trying to deserialize some json from the mixpanel data api using Newtonsoft Json, I've created a class, but I get errors. The problem lies with the fact that property A and B in the 'values' object aren't fixed names. Hence why I thought using Dictionary<string, NameValueCollection> would work. Any help with this would be great
JSON:
{
"legend_size": 1,
"data": {
"series": [
"2014-06-30"
],
"values": {
"A": {
"2014-06-30": 1082,
"2014-06-23": 4249
},
"B": {
"2014-06-30": 1082,
"2014-06-23": 4249
}
}
}
}
Results.cs
public class Result
{
[JsonProperty("data")]
public Data Data { get; set; }
[JsonProperty("legend_size")]
public int LegendSize { get; set; }
}
public class Data
{
[JsonProperty("series")]
public IEnumerable<DateTime> Series { get; set; }
[JsonConverter(typeof(MixEventValuesConverter))]
[JsonProperty("values")]
public IDictionary<string, NameValueCollection> Values { get; set; }
}
I created a converter (for the first time ever, i don't really know what I'm doing with it!) and tried to return just an empty value for now, but I get the error 'Additional text found in JSON string after finishing deserializing object.'.
MixEventValuesConverter.cs
public class MixEventValuesConverter : 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 col = new NameValueCollection();
col.Add("testtest", "vallll");
var dic = new Dictionary<string, NameValueCollection>();
dic.Add("testt", col);
return dic;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(MixEventResult);
}
}

You don't need a converter here; just use a nested Dictionary like this:
public class Data
{
[JsonProperty("series")]
public IEnumerable<DateTime> Series { get; set; }
[JsonProperty("values")]
public IDictionary<string, IDictionary<DateTime, int>> Values { get; set; }
}

Related

Use JsonConverter to deserialize JToken to bool

I have an odd JSON object I need to deserialize.
{
"data": {
"id": 123,
"permissions": {
"appName": {
"data": {
"1": {
"2021-08-01": {
"2020": {
"isAllowed": {}
}
}
}
}
}
}
}
}
I've gotten most of it to work with a wrapper and nested dictionaries but the final part isn't working as expected. It works fine if I create a class like this, but using it is clunky.
public class DataWrapper<T>
{
public T Data { get; set;}
}
public class PermissionValues
{
public JToken IsAllowed { get; set; }
}
DataWrapper<Dictionary<int, Dictionary<DateTime, Dictionary<int, PermissionValues>>>>
I'd like to change JToken to a bool. When I do that and add a JsonConverter
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return true; //{} is true but JSON.net parses as null, old was: reader.Value != null;
}
I get an error:
Newtonsoft.Json.JsonSerializationException: 'Could not convert string
'isAllowed' to dictionary key type 'System.Int32'. Create a
TypeConverter to convert from the string to the key type object. Path
'data.permissions.appName.data.1.2021-08-01.2020.isAllowed'
Any idea what I am missing?
Use JToken.ToObject()
string json = "{\"data\":{\"id\":123,\"permissions\":{\"appName\":{\"data\":{\"1\":{\"2021-08-01\":{\"2020\":{\"isAllowed\":{}}}}}}}}}";
var jToken= JToken.Parse(json).SelectToken("$.data.permissions.appName");
var result = jToken.ToObject<DataWrapper<Dictionary<int, Dictionary<DateTime, Dictionary<int, PermissionValues>>>>>();
Use the JsonConvertor Attribute on IsAllowed Property.
public class DataWrapper<T>
{
public T Data { get; set; }
}
public class PermissionValues
{
[JsonConverter(typeof(BoolConverter))]
public bool IsAllowed { get; set; }
}
public class BoolConverter : JsonConverter
{
//Implement other abstract functions
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return reader.Value != null;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(bool);
}
}

Use Json Converter to add properties to Dictionary

I am deserializing some JSON endpoints to C# Objects via JArray.Parse() and have come across a schema I'm not sure how to handle. The nUnavailResReasonsMap Object below has a dynamic number of name/value pairs; all of the name values will be integers:
{
"id": "Customer Service",
"operation": "UPDATE",
"VoiceIAQStats": {
"id": 139,
"esdId": 139,
...
"nUnavailResources": 2,
"nUnavailResReasonsMap": {
"4": 1,
"9": 1
},
"nWorkResources": 0,
"nSelectedResources": 0,
...
"nSLAPercentageHighThreshold": 0
}
}
And here are my C# Objects:
//Root Level
public class QueueStats
{
public string Id { get; set; }
public string Operation { get; set; }
public VoiceIaqStats VoiceIaqStats { get ; set ; }
}
public class VoiceIaqStats
{
public int Id { get; set; }
public int EsdId { get; set; }
...
public int NUnavailResources { get; set; }
public NUnavailResReasonsMaps NUnavailResReasonsMap { get ; set ; }
public int NWorkResources { get; set; }
public int NSelectedResources { get; set; }
...
public int NSlaPercentageHighThreshold { get; set; }
}
[JsonConverter( typeof( QueueStatsConverter))]
public class NUnavailResReasonsMaps
{
public Dictionary<string ,int > NUnavailResReasonsMap { get; set; }
}
Per another SO Post, I have setup the below Json Converter, which is getting called but I'm not sure how to get the values into the Dictionary defined above.
public class QueueStatsConverter : JsonConverter
{
public override bool CanConvert( Type objectType)
{
return objectType.IsClass;
}
public override bool CanWrite => false;
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var instance = objectType.GetConstructor(Type .EmptyTypes)?.Invoke(null);
var props = objectType.GetProperties();
var jo = JObject.Load(reader);
foreach ( var jp in jo.Properties())
{
//I can see the properties, but how do I add them to my existing dictionary?
}
return instance;
}
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Is there a way to make this work or do I need to change my approach?
You don't need a special converter at all, just remove the NUnavailResReasonsMaps class and use a Dictionary<int, int> instead:
public class VoiceIaqStats
{
//snip
public Dictionary<int, int> NUnavailResReasonsMap { get; set; }
//snip
}
And the conversion will work out of the box:
var result = JsonConvert.DeserializeObject<QueueStats>(json);

Newtonsoft JsonConvert.DeserializeObject how to ignore empty objects

I have the following class definitions:
public class Tag
{
public Guid? TagId { get; set; }
public string TagText { get; set; }
public DateTime CreatedOn { get; set; }
}
public class Wiki
{
public Guid? WikiId { get; set; }
public string WikiText { get; set; }
public string Title { get; set; }
public DateTime CreatedOn { get; set; }
public IEnumerable<Tag> Tags { get; set; }
}
From the database i get the following json Object:
{
"WikiId": "83981284-0AD3-4420-90AB-15E3BF6BD7B7",
"WikiText": "Text",
"Title": "Title",
"CreatedOn": "2017-08-07T09:16:06.0800000",
"Tags": [{}] // <-- here i would like to ignore the empty Tag object
}
When i do now a JsonConvert.DeserializeObject<Wiki>(json) i get a Wiki object with a list of 1 Tag with the values TagId: null, TagText: null and CreatedOn: "0001-01-01T00:00:00"
Is there a way to ignore the empty Tag object while deserializing? I have tried several JsonSerializerSettings but nothing helped.
You could use a custom JsonConverter to ignore the empty objects during deserialization. Something like this could work:
class IgnoreEmptyItemsConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
List<T> list = new List<T>();
JArray array = JArray.Load(reader);
foreach (JObject obj in array.Children<JObject>())
{
if (obj.HasValues)
{
list.Add(obj.ToObject<T>(serializer));
}
}
return list;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, just add a [JsonConverter] attribute to your Tags property like this:
public class Wiki
{
...
[JsonConverter(typeof(IgnoreEmptyItemsConverter<Tag>))]
public IEnumerable<Tag> Tags { get; set; }
}
Fiddle: https://dotnetfiddle.net/hrAFsh
You'll have to detect the empty tag objects post-conversion and remove them yourself. From the deserializer's perspective, {} is a perfectly valid and complete Tag object whose properties are all unset.
Something like the following should work (presuming C# 6):
Wiki wiki = JsonConvert.DeserializeObject<Wiki>(json);
wiki.Tags = Wiki.Tags?.Where(x => x.TagId.HasValue)?.ToList();
Thanks to #Brian-Rogers's brilliant answer, I was able to come up with a non-generic solution which will work on all collections instead of List only:
public class IgnoreEmptyArrayItemsConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
bool result = typeof(System.Collections.IEnumerable).IsAssignableFrom(objectType);
return result;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenIndexesToRemove = new List<int>();
var array = JArray.Load(reader);
for (int i = 0; i < array.Count; i++)
{
var obj = array[i];
if (!obj.HasValues)
tokenIndexesToRemove.Add(i);
}
foreach (int index in tokenIndexesToRemove)
array.RemoveAt(index);
var result = array.ToObject(objectType, serializer);
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Instead of looping through the objects, deserializing them and adding them to a hardcoded List collection one by one, this solution will just remove the faulting tokens from the JArray and let the library deserialize the whole array to the type it should be.
Usage:
public class Wiki
{
...
[JsonConverter(typeof(IgnoreEmptyItemsConverter))] // No generic parameter needed
public HashSet<Tag> Tags { get; set; }
}
Fiddle: https://dotnetfiddle.net/9FCcpD

Simplifying Json entity for deserialisation

Actually I receive Json like this:
{
"id" : 1,
"items": [
{
"media": {
"id": 1,
}
},
{
"media": {
"id": 2,
},
}
],
"status": "ok"
}
I created class like this to map on entity
public class Message
{
[JsonProperty("items")]
public List<Item> Items { get; set; }
}
public class Item
{
[JsonProperty("media")]
public Media Media { get; set; }
}
public class Media
{
[JsonProperty("id")]
public string Id { get; set; }
}
This work perfectly but my question is:
Is it possible to remove Item class and directly cast in List of Media to simplify ?
Update:
I'm pretty sure it is possible, the first way I found before post is JSonConverter, but I'm not sure is the best way to do that, i would like to know if there are any easier solution, with attribute for example.
Using a private Dictionary
Yes, you can, for example using a (private) Dictionary
public class Message
{
public List<Media> Items { get; set; }
}
public class Media
{
[JsonProperty("media")]
private Dictionary<string, string> IdDict;
public string Id
{
get { return IdDict["id"]; }
set { IdDict["id"] = value; }
}
}
Usage
var result = JsonConvert.DeserializeObject<Message>(json);
var test = result.Items[1].Id;
Implementing a Converter
Alternatively you can achieve your result by implementing a Converter
public class MediaConverter : JsonCreationConverter<Message>
{
protected override Message Create(Type objectType, JObject jObject)
{
var msg = new Message();
msg.Items = new List<Media>();
foreach (var item in jObject["items"])
{
Media media = new Media();
media.Id = item["media"]["id"].Value<string>();
msg.Items.Add(media);
}
return msg;
}
}
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
return target;
}
public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Usage
var result = JsonConvert.DeserializeObject<Message>(json, new MediaConverter());
var test = result.Items[1].Id;

Deserializing a JSON Dictionary<int,CustomType> to List<CustomType>

I need to Deserialize a JSON Dictionary of type Dictionary to a List (List), I am using JSON.Net for this purpose. For sure its an amazing Library its just this that I am bit stuck !
I am subscribing to some API the Response is As you can see below:
"team_details": {
"0": {
"index": 1,
"team_id": "1..",
"team_name": "Research Team",
"team_url": "...",
"role": "Administrator"
},
"1": {
"index": 2,
"team_id": "2..",
"team_name": "WB Team",
"team_url": "...",
"role": "User"
}
}
I need to use this to convert it to List<Team> where Teams:
Class Teams{
public int Index{get;set;}
public String TeamName{get;set;}
...
}
The easiest way to do this is to deserialize into a Dictionary<string, Team> and then get the values into a list using dictionary.Values.ToList() when you need them.
However, if you would really rather have a List<Team> in your class definition, you can use a custom JsonConverter to do the conversion during deserialization.
public class TeamListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<Team>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
return token.Values().Select(v => v.ToObject<Team>()).ToList();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Demo:
class Program
{
static void Main(string[] args)
{
string json = #"
{
""team_details"": {
""0"": {
""index"": 1,
""team_id"": ""1.."",
""team_name"": ""Research Team"",
""team_url"": ""..."",
""role"": ""Administrator""
},
""1"": {
""index"": 2,
""team_id"": ""2.."",
""team_name"": ""WB Team"",
""team_url"": ""..."",
""role"": ""User""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
foreach (Team team in root.Teams)
{
Console.WriteLine(team.TeamName);
}
}
}
public class RootObject
{
[JsonProperty("team_details")]
[JsonConverter(typeof(TeamListConverter))]
public List<Team> Teams { get; set; }
}
public class Team
{
[JsonProperty("index")]
public int Index { get; set; }
[JsonProperty("team_id")]
public string TeamId { get; set; }
[JsonProperty("team_name")]
public string TeamName { get; set; }
[JsonProperty("team_url")]
public string TeamUrl { get; set; }
[JsonProperty("role")]
public string Role { get; set; }
}
Output:
Research Team
WB Team

Categories

Resources