I'm sorry for my ignorance since I'm a newbie. I need to check if a twitch stream is live or not. I'm doing this by using HttpClient and GET request. The class TwitchData deserialize JSON as object is the following.
public partial class TwitchData
{
[JsonProperty("data")]
public Datum[] Data { get; set; }
[JsonProperty("pagination")]
public Pagination Pagination { get; set; }
}
public partial class Datum
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("user_id")]
[JsonConverter(typeof(ParseStringConverter))]
public long UserId { get; set; }
[JsonProperty("game_id")]
[JsonConverter(typeof(ParseStringConverter))]
public long GameId { get; set; }
[JsonProperty("community_ids")]
public object[] CommunityIds { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("viewer_count")]
public long ViewerCount { get; set; }
[JsonProperty("started_at")]
public DateTimeOffset StartedAt { get; set; }
[JsonProperty("language")]
public string Language { get; set; }
[JsonProperty("thumbnail_url")]
public string ThumbnailUrl { get; set; }
}
public partial class Pagination
{
[JsonProperty("cursor")]
public string Cursor { get; set; }
}
public partial class TwitchData
{
public static TwitchData FromJson(string json) => JsonConvert.DeserializeObject<TwitchData>(json, QuickType.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this TwitchData self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters = {
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class ParseStringConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(long) || t == typeof(long?);
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);
long l;
if (Int64.TryParse(value, out l))
{
return l;
}
throw new Exception("Cannot unmarshal type long");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (long)untypedValue;
serializer.Serialize(writer, value.ToString());
return;
}
public static readonly ParseStringConverter Singleton = new ParseStringConverter();
}
and I do the request with this
HttpClient client = new HttpClient();
string uri = "https://api.twitch.tv/helix/streams?user_id=59980349";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Client-ID", token);
var result = client.GetStringAsync(uri);
jsonString = result.ToString();
twitchData = PwdResetRequest.FromJson(jsonString);
The problem with this is that the JSON changes if the stream is offline thus making the written class TwitchData useless to be used for JsonConvert.DeserializeObject.
Following are the JSONs for when the stream is online and offline.
{
"data": [
{
"id": "30356128676",
"user_id": "59788312",
"game_id": "498652",
"community_ids": [],
"type": "live",
"title": "A stream",
"viewer_count": 1325,
"started_at": "2018-09-07T16:30:09Z",
"language": "en",
"thumbnail_url": "url"
}
],
"pagination": {
"cursor": "eydBIjpwdWGsLaJhIjp7IkGH4hNl6CH6MXr9"
}
}
and when its offline
{
"data": [],
"pagination": {}
}
I found a solution to my problem. The object I'm creating has 2 variables, Datum[] data and Pagination Pagination. Frankly, if the stream is offline, only change in JSON string (which can be seen in the question) is that both data array and pagination block is empty. By checking the condition of length of these variables (either checking one of them is enough), I can decide if stream is live or offline. For instance for getStreams class,
if (getStreams.Datum.Length != 0) {
return true;
}
else {
return false;
}
Related
I have the below JSON coming from API
{
"rates": {
"EURGBP": {
"rate": 0.871625,
"timestamp": 1619193484
},
"USDJPY": {
"rate": 107.939463,
"timestamp": 1619193484
}
}
}
I want to create classes to deserialize this JSON. If I do JSON to C# conversion I get the below C# classes:
public class Rates
{
public EURGBP EURGBP { get; set; }
public USDJPY USDJPY { get; set; }
}
public class EURGBP
{
public double rate { get; set; }
public int timestamp { get; set; }
}
public class USDJPY
{
public double rate { get; set; }
public int timestamp { get; set; }
}
The problem is there might be more objects coming in rates object. Like
"NZDUSD": {
"rate": 0.718962,
"timestamp": 1619198884
}
In this case I won't be having a class NZDUSD to deserialize the new object.
So can I have a generic class using which I can deserialize the objects under rates since they will be having the common properties?
OR
Can I convert those objects under rates into List or Dictionary while or after deserializing?
You could create an object model like this using a dictionary:
public class Result
{
public IDictionary<string, Rate> Rates { get; set; }
}
public class Rate
{
public double rate { get; set; }
public int timestamp { get; set; }
}
and then deserialize using Newtonsoft:
var values = Newtonsoft.Json.JsonConvert.DeserializeObject<Result>(json);
So can I have a generic class using which I can deserialize the objects under rates since they will be having the common properties?
There are a few different way's you can accomplish this, one of which was already offered by Connell. Another way you can do this is by creating your own JsonConverter. Below I will show you how you can do this.
-Create a new class called: CurrencyConverter make sure it's like below:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class CurrencyConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => typeof(List<Rate>).IsAssignableFrom(objectType);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
List<Rate> rates = new List<Rate>();
if (reader.TokenType == JsonToken.StartObject)
{
JToken token = JToken.Load(reader);
IEnumerable<JToken> tokenChildren = token.Root.First?.First?.Children();
foreach (JToken child in tokenChildren)
{
double dblRate = GetValue(child.First, "rate", 0d);
int intTimeStamp = GetValue(child.First, "timestamp", 0);
rates.Add(new Rate() { RateName = ((JProperty)child).Name, rate = dblRate, Timestamp = intTimeStamp });
}
}
return rates;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public T GetValue<T>(JToken jToken, string key, T defaultValue = default(T))
{
dynamic ret = jToken[key];
if (ret == null) return defaultValue;
if (ret is JObject) return JsonConvert.DeserializeObject<T>(ret.ToString());
return (T)ret;
}
}
-Next create a new class called Rate:
public class Rate
{
public string RateName { get; set; }
public double rate { get; set; }
public int Timestamp { get; set; }
}
-Finally deserialize your json:
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.Converters.Add(new CurrencyConverter());
var jsonOutput = JsonConvert.DeserializeObject<List<Rate>>(json, jsonSerializerSettings);
Using the json you provided, I got back a list that had two Rate objects.
This question already has answers here:
How to handle both a single item and an array for the same property using JSON.net
(9 answers)
Closed 3 years ago.
I am trying to process an object that can be either an array of object or just the object. Using the code below only works when the naics is an object and not an array. What am I doing wrong?
Here is the shortest example I can come up with:
[{
"section": "52.219-1.b",
"naics": [{
"naicsName": "Engineering Services",
"isPrimary": true,
"ExcpCounter": 1,
"isSmallBusiness": "Y",
"naicsCode": 541330
},
{
"naicsName": "Military and Aerospace Equipment and Military Weapons",
"isPrimary": true,
"ExcpCounter": 2,
"isSmallBusiness": "Y",
"naicsCode": 541330
}
]
},
{
"section": "52.219-1.b",
"naics": {
"naicsName": "Janitorial Services",
"isPrimary": true,
"isSmallBusiness": "Y",
"naicsCode": 561720
}
}
]
I will only have one of the types but I forced two in an array to force it into Quick Type.
My classes are:
[JsonProperty("naics", NullValueHandling = NullValueHandling.Ignore)]
public AnswerNaics Naics { get; set; }
public partial struct AnswerNaics
{
public AnswerNaic Naic;
public AnswerNaic[] NaicArray;
public static implicit operator AnswerNaics(AnswerNaic Naic) => new AnswerNaics { Naic = Naic };
public static implicit operator AnswerNaics(AnswerNaic[] NaicArray) => new AnswerNaics { NaicArray = NaicArray };
}
public partial class AnswerNaic
{
[JsonProperty("naicsName")]
public string NaicsName { get; set; }
[JsonProperty("hasSizeChanged")]
public string HasSizeChanged { get; set; }
[JsonProperty("isPrimary")]
public bool IsPrimary { get; set; }
[JsonProperty("ExcpCounter", NullValueHandling = NullValueHandling.Ignore)]
public long? ExcpCounter { get; set; }
[JsonProperty("isSmallBusiness")]
public string IsSmallBusiness { get; set; }
[JsonProperty("naicsCode")]
public string NaicsCode { get; set; }
}
internal class NaicsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(AnswerNaics) || t == typeof(AnswerNaics?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<AnswerNaic>(reader);
return new AnswerNaics { Naic = objectValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<AnswerNaic[]>(reader);
return new AnswerNaics { NaicArray = arrayValue };
}
throw new Exception("Cannot unmarshal type AnswerNaics");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (AnswerNaics)untypedValue;
if (value.NaicArray != null)
{
serializer.Serialize(writer, value.NaicArray);
return;
}
if (value.Naic != null)
{
serializer.Serialize(writer, value.Naic);
return;
}
throw new Exception("Cannot marshal type Naics");
}
public static readonly NaicsConverter Singleton = new NaicsConverter();
}
I have more object or array nodes, but I am just trying to figure out one to be able to apply to all of them.
Since you cannot change the incoming JSON, you're going to need a custom converter instead. Something like this for example:
public class NaicsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Naics);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var naics = new Naics();
switch (reader.TokenType)
{
case JsonToken.StartObject:
// We know this is an object, so serialise a single Naics
naics.Add(serializer.Deserialize<Naic>(reader));
break;
case JsonToken.StartArray:
// We know this is an object, so serialise multiple Naics
foreach(var naic in serializer.Deserialize<List<Naic>>(reader))
{
naics.Add(naic);
}
break;
}
return naics;
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And the supporting classes:
public class Root
{
public string Section { get; set; }
[JsonConverter(typeof(NaicsConverter))]
public Naics Naics { get; set; }
}
// This isn't ideal, but it's quick and dirty and should get you started
public class Naics : List<Naic>
{
}
public class Naic
{
public string NaicsName { get; set; }
public bool IsPrimary { get; set; }
public string IsSmallBusiness { get; set; }
public long NaicsCode { get; set; }
}
And finally, to deserialise:
var settings = new JsonSerializerSettings {Converters = {new NaicsConverter()}};
var root = JsonConvert.DeserializeObject<Root[]>(Json, settings);
Now your object will get serialised into the list, but as a single item.
You can solve this using a dynamic field in your class.
Consider this JSON:
[
{
"field1": "val1",
"nested": [
{
"nestedField": "val2"
},
{
"nestedField": "val3"
}
]
},
{
"field1": "val4",
"nested":
{
"nestedField": "val5"
}
}
]
nested field is first an array with 2 objects and in the second appearance a single object. (similar to the JSON you posted)
So the class representation would look like:
public class RootObject
{
public string field1 { get; set; }
public dynamic nested { get; set; }
public List<NestedObject> NestedObjects
{
get
{
if(nested is JArray)
{
return JsonConvert.DeserializeObject<List<NestedObject>>(nested.ToString());
}
var obj = JsonConvert.DeserializeObject<NestedObject>(nested.ToString());
return new List<NestedObject> { obj };
}
}
}
public class NestedObject
{
public string nestedField { get; set; }
}
The Deserialization code is trivial using Newtonsoft JSON:
var objectList = JsonConvert.DeserializeObject<List<RootObject>>("some_json");
foreach(var v in objectList)
{
foreach(var n in v.NestedObjects)
{
Console.WriteLine(n.nestedField);
}
}
The only change is an implementation of NestedObjects ready only property. It check if the dynamic object is JArray or object. In any case, it returns a List of nested objects.
Excuse my ignorance since I'm a newbie. I'm trying to get stream info from Twitch whether the stream is live or not. I'm doing this by using HttpClient and GET request. The class TwitchData deserialize JSON as object is the following.
public partial class TwitchData
{
[JsonProperty("data")]
public Datum[] Data { get; set; }
[JsonProperty("pagination")]
public Pagination Pagination { get; set; }
}
public partial class Datum
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("user_id")]
[JsonConverter(typeof(ParseStringConverter))]
public long UserId { get; set; }
[JsonProperty("game_id")]
[JsonConverter(typeof(ParseStringConverter))]
public long GameId { get; set; }
[JsonProperty("community_ids")]
public object[] CommunityIds { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("viewer_count")]
public long ViewerCount { get; set; }
[JsonProperty("started_at")]
public DateTimeOffset StartedAt { get; set; }
[JsonProperty("language")]
public string Language { get; set; }
[JsonProperty("thumbnail_url")]
public string ThumbnailUrl { get; set; }
}
public partial class Pagination
{
[JsonProperty("cursor")]
public string Cursor { get; set; }
}
public partial class TwitchData
{
public static TwitchData FromJson(string json) => JsonConvert.DeserializeObject<TwitchData>(json, QuickType.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this TwitchData self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters = {
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class ParseStringConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(long) || t == typeof(long?);
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);
long l;
if (Int64.TryParse(value, out l))
{
return l;
}
throw new Exception("Cannot unmarshal type long");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (long)untypedValue;
serializer.Serialize(writer, value.ToString());
return;
}
public static readonly ParseStringConverter Singleton = new ParseStringConverter();
}
I do the HttpClient GET request with the following
HttpClient client = new HttpClient();
string uri = "https://api.twitch.tv/helix/streams?user_id=59980349";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Client-ID", token);
var result = client.GetStringAsync(uri);
jsonString = result.ToString();
twitchData = PwdResetRequest.FromJson(jsonString);
When this is run, Newtonsoft.Json.JsonReaderException, Unexpected character encountered while parsing value: S. Path '', line 0, position 0 is thrown. Further debugging, I found that the program breaks at line 71 which is
public static TwitchData FromJson(string json) => JsonConvert.DeserializeObject<TwitchData>(json, QuickType.Converter.Settings);
since I got this class from json2csharp.com, I don't know how to fix that line or why the Exception is thrown.
EDIT:
JSON is requested in comments. This is the JSON if the stream is live
{
"data": [
{
"id": "30356128676",
"user_id": "59788312",
"game_id": "498652",
"community_ids": [],
"type": "live",
"title": "A stream",
"viewer_count": 1325,
"started_at": "2018-09-07T16:30:09Z",
"language": "en",
"thumbnail_url": "url"
}
],
"pagination": {
"cursor": "eydBIjpwdWGsLaJhIjp7IkGH4hNl6CH6MXr9"
}
}
and when its offline
{
"data": [],
"pagination": {}
}
I have a JSON object that will be formatted as such:
{
"myNodes": [
{
"param1": 1,
"param2": "myValue2a",
"param3": {
"myParam3param": 0
}
},
{
"param1": 1,
"param2": "myValue2b",
"param3": [
{
"myItemA": "abc",
"myItemB": "def",
"myItemC": "0"
}]
},
{
"param1": 1,
"param2": "myValue2c",
"param3": [
{
"myItemA": "ghi",
"myItemB": "jkl",
"myItemC": "0"
}]
}]
}
In C#, I'm wondering how to structure response objects to handle this. I'm guessing I'll have some sort of parent class or interface that contains param1, param2, and param3. However, param3 will need to be declared as type "object", and sometimes it will be an object w/ myParam3param, and other times it will be a list of things. I'm also guessing that I should use child classes that determine what type param3 is.
Is this possible? How should this be accomplished... an abstract class or interface?
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class ChildClasses
{
[JsonProperty("myNodes")]
public List<MyNode> MyNodes { get; set; }
}
public partial class MyNode
{
[JsonProperty("param1")]
public long Param1 { get; set; }
[JsonProperty("param2")]
public string Param2 { get; set; }
[JsonProperty("param3")]
public Param3Union Param3 { get; set; }
}
public partial class Param3Element
{
[JsonProperty("myItemA")]
public string MyItemA { get; set; }
[JsonProperty("myItemB")]
public string MyItemB { get; set; }
[JsonProperty("myItemC")]
public string MyItemC { get; set; }
}
public partial class PurpleParam3
{
[JsonProperty("myParam3param")]
public long MyParam3Param { get; set; }
}
public partial struct Param3Union
{
public List<Param3Element> Param3ElementArray;
public PurpleParam3 PurpleParam3;
public bool IsNull => Param3ElementArray == null && PurpleParam3 == null;
}
public partial class ChildClasses
{
public static ChildClasses FromJson(string json) => JsonConvert.DeserializeObject<ChildClasses>(json, QuickType.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this ChildClasses self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters = {
new Param3UnionConverter(),
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class Param3UnionConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Param3Union) || t == typeof(Param3Union?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
switch (reader.TokenType)
{
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<PurpleParam3>(reader);
return new Param3Union { PurpleParam3 = objectValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<List<Param3Element>>(reader);
return new Param3Union { Param3ElementArray = arrayValue };
}
throw new Exception("Cannot unmarshal type Param3Union");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (Param3Union)untypedValue;
if (value.Param3ElementArray != null)
{
serializer.Serialize(writer, value.Param3ElementArray); return;
}
if (value.PurpleParam3 != null)
{
serializer.Serialize(writer, value.PurpleParam3); return;
}
throw new Exception("Cannot marshal type Param3Union");
}
}`
I have a Web Api Controller like this one :
public IHttpActionResult Create(PaymentDTO Payment)
My DTOs are:
public class PaymentDTO
{
public int Id { get; set; }
public string type { get; set; }
public IEnumerable<TransactionDTO> Transactions { get; set; }
}
public class TransactionDTO
{
public int Id { get; set; }
public string Description { get; set; }
public string CreateTime { get; set; }
public string UpdateTime { get; set; }
}
public class SaleDTO : TransactionDTO
{
public string Total { get; set; }
public string Currency{ get; set; }
}
public class OrderDTO : TransactionDTO
{
public string State {get;set;}
}
I receive the following JSON formatted data :
{
"Type": "sale",
"Id": 101,
"transactions": [
{
"Total": "30.50",
"Currency": "USD",
"Description": "transaction description"
}
]
}
I want JSON.net to instantiate either a IEnumerable<SaleDTO> or IEnumerable<OrderDTO> based on the Type Property.
I could've used a custom type converter, but only if Type property was in TransactionDTO. But I want the Type property to be in the parent object (PaymentDTO)
Thank you in advance for your help.
You can do this with a custom JsonConverter on the PaymentDTO class:
public class PaymentDTOConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(PaymentDTO).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var obj = JObject.Load(reader);
var payment = (PaymentDTO)existingValue ?? new PaymentDTO();
// Extract the transactions.
var transactions = obj.Property("transactions") ?? obj.Property("Transactions");
if (transactions != null)
transactions.Remove();
// Populate the remaining regular properties.
using (var subReader = obj.CreateReader())
serializer.Populate(subReader, payment);
if (transactions != null)
{
// Deserialize the transactions list.
var type = PaymentDTO.GetTransactionDTOType(payment.type) ?? typeof(TransactionDTO);
using (var subReader = transactions.Value.CreateReader())
// Here we are taking advantage of array covariance.
payment.Transactions = (IEnumerable<TransactionDTO>)serializer.Deserialize(subReader, type.MakeArrayType());
}
return payment;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then apply it to your PaymentDTO class as follows:
[JsonConverter(typeof(PaymentDTOConverter))]
public class PaymentDTO
{
static Dictionary<string, Type> namesToTransactions;
static Dictionary<Type, string> transactionsToNames = new Dictionary<Type, string>
{
{ typeof(SaleDTO), "sale" },
{ typeof(OrderDTO), "order" },
};
static PaymentDTO()
{
namesToTransactions = transactionsToNames.ToDictionary(p => p.Value, p => p.Key);
}
public static string GetTransactionDTOTypeName<TTransactionDTO>() where TTransactionDTO : TransactionDTO
{
string name;
if (transactionsToNames.TryGetValue(typeof(TTransactionDTO), out name))
return name;
return null;
}
public static Type GetTransactionDTOType(string name)
{
Type type;
if (namesToTransactions.TryGetValue(name, out type))
return type;
return null;
}
public int Id { get; set; }
public string type { get; set; }
[JsonProperty("transactions")]
public IEnumerable<TransactionDTO> Transactions { get; set; }
}