Parse JSON in response to the existing request - c#

Hi I have a response from the web service and i would like to have a single url from the response.
My response is in the below format.
[{"cdn_streaming_uri": "9e849cfbb2e157-22558a0600b387d0abe240fe5.r73.stream..rackcdn.com", "name": "test1", "cdn_ios_uri": "d3d4c27-22558a0600b387d0abc071d0ae240kcdn.com", "cdn_ssl_uri": "https://990fea26e-22558a0600b387d0abc071d0ae240fe5.ssl.cdn.com", "cdn_enabled": false, "ttl": 259200, "log_retention": false, "cdn_uri": "99b56a009-22558a0600b3c071d0ae240fe5.r73.n.com"}, {"cdn_streaming_uri": "74ec8c-d5edc6cad91792413b1b134fde.r46.stcdn.com", "name": "test2", "cdn_ios_uri": "d05437e44-d5edc61792413b1b134fde.iosr.cdn.com", "cdn_ssl_uri": "https://a1c2ebbf5-d5edc6cd91792413b1b134fde.scdn.com", "cdn_enabled": false, "ttl": 259200, "log_retention": false, "cdn_uri": "72ffd-d5edc6ca16852413b1b134fde.cdn.com"}, {"cdn_streaming_uri": "93665b76-550971032c2a22cdn.com", "name": "test3", "cdn_ios_uri": "ca6b-550971032c2fbf19452d6a.iosr.cf2.rackcdn.com", "cdn_ssl_uri": "https://c7c39-550971032cbf19452d6cdn.com", "cdn_enabled": true, "ttl": 86400, "log_retention": true, "cdn_uri": "68fc6d831a94-550971032c252d6a.r3cdn.com"}]
I need to the "cdn_streaming_uri" for the name "test3".
You can view the JSON parser in http://json.parser.online.fr/
How do i parse it?
Here is my code:
public static object getTokenResponse(String PrivateURL, string ResponseType)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(PrivateURL +"?format=JSON");
request.Method = "GET";
request.Headers.Add("X-Auth-Token", id);
//request.ContentType = "application/XML";
HttpWebResponse resp1;
try
{
resp1 = (HttpWebResponse)request.GetResponse();
}
catch (Exception exp)
{
string[] st = new string[0];
return st;
}
StreamReader reader = new StreamReader(resp1.GetResponseStream());
string secondresponse = reader.ReadToEnd();
Console.WriteLine(secondresponse);
reader.Close();
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
object obj1 = json_serializer.DeserializeObject(secondresponse);
}
I could see the response in obj1.

The best approach, I think, is to create class which will represent your response. The easiest way is to use Visual Studio's EDIT -> Paste Special -> Paste JSON As Classes option:
You just copy your response and paste it as JSON classes. Visual studio will generate model for you. In this particular case the result will be:
namespace ConsoleApplication91
{
public class Rootobject
{
public Class1[] Property1 { get; set; }
}
public class Class1
{
public string cdn_streaming_uri { get; set; }
public string name { get; set; }
public string cdn_ios_uri { get; set; }
public string cdn_ssl_uri { get; set; }
public bool cdn_enabled { get; set; }
public int ttl { get; set; }
public bool log_retention { get; set; }
public string cdn_uri { get; set; }
}
}
which, of course, does not look very nice, but you're always welcome to refactor this code. When you have your model you download your response, parse it and get what you need using linq, for example:
using (var client = new WebClient())
{
var url = "your service url";
var serializer = new JavaScriptSerializer();
// Response in JSON format
var respJson = client.DownloadString(url);
// Deserialized response
var resp = serializer.Deserialize<Rootobject>(respJson);
// Your requested result
var result = resp.Property1.FirstOrDefault(o => o.name == "test3").cdn_streaming_uri;
}
EDITS:
After refactoring (using DataMember attributes and removing redundant model objects) you can have the following model:
[DataContract]
public class Model
{
[DataMember(Name = "cdn_streaming_uri")]
public string CdnStreamingUri { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "cdn_ios_uri")]
public string CdnIosUri { get; set; }
[DataMember(Name = "cdn_ssl_uri")]
public string CdnSslUri { get; set; }
[DataMember(Name = "cdn_enabled")]
public bool CdnEnabled { get; set; }
[DataMember(Name = "ttl")]
public int Ttl { get; set; }
[DataMember(Name = "log_retention")]
public bool LogRetention { get; set; }
[DataMember(Name = "cdn_uri")]
public string CdnUri { get; set; }
}
After some research I've figured out that JavascriptSerializer is a little deprecated and it does not support any kind of DataMemberAttributes. So I would recommend to use DataContractJsonSerializer. It is little messier than JavascriptSerializer but I think it's fine. If you do not care about any code conventions you can peacefully use the firs provided option (with JavaScriptSerializer and no DataMember attributes). And do not forget to update result query:
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(respJson)))
{
var serializer = new DataContractJsonSerializer(typeof(Model[]));
var resp = serializer.ReadObject(ms) as Model[];
var result = resp.FirstOrDefault(o => o.Name == "test3").CdnStreamingUri;
}
But, also, if you don't want to use linq (which I'm still strongly recommend) you can create some function which will find the CdnStreamingUri you need:
public static class Extensions
{
public static string GetCdnStreamingUriFor(this Model[] input, string name)
{
foreach (var model in input)
{
if (model.Name == name)
return model.CdnStreamingUri;
}
return string.Empty;
}
}
And your result query will look like:
var result = resp.GetCdnStreamingUriFor("test3");
P.S.
Full list of all used namespaces:
using System;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Runtime.Serialization;

I recommend you to generate class for that json, like the following (http://json2csharp.com/ ):
public class RootObject
{
public string cdn_streaming_uri { get; set; }
public string name { get; set; }
public string cdn_ios_uri { get; set; }
public string cdn_ssl_uri { get; set; }
public bool cdn_enabled { get; set; }
public int ttl { get; set; }
public bool log_retention { get; set; }
public string cdn_uri { get; set; }
}
After that you can deserialize strongly typed object http://msdn.microsoft.com/en-us/library/bb355316(v=vs.110).aspx

Related

How to deserialize json object with no parent class?

I'm trying and failing to write a program that will make an API call and then turn the returned items into objects that fit my model. Specifically I can't make it deserealize, and I suspect it has something to do with how the json is return compared to what my model looks like.
The data I'm trying to get looks like this;
https://api.nasa.gov/planetary/apod?start_date=2022-03-01&end_date=2022-03-08&api_key=DEMO_KEY
As you can see, it consists of an array of items, but there is no name for the array items. When I paste this into the Get-model with Paste JSON as Classes, I get this;
public class GetApodItemsResult
{
public Class1[] Property1 { get; set; }
}
public class Class1
{
public string copyright { get; set; }
public string date { get; set; }
public string explanation { get; set; }
public string hdurl { get; set; }
public string media_type { get; set; }
public string service_version { get; set; }
public string title { get; set; }
public string url { get; set; }
}
My entire code works just fine up until I need to serialize the JSON with this line:
var responseObject = await response.Content.ReadFromJsonAsync<GetApodItemsResult>();
, where I get this message;
System.Text.Json.JsonException: 'The JSON value could not be converted to UnnamedSpaceProject.Models.GetApodItemsResult.
Interestingly I know that the code works on a spotify api call, so the code really should work largely the same, which leads me to believe that the problem is with how the JSON is formatted.
How do I get around that? Because I don't see a way to have the root object contain an unnamed array.
Your GetApodItemsResult class is not a valid class to deserialize the content you get from server, the correct deserialization type will be List<Class1> or Class1[]
var responseObject = await response.Content.ReadFromJsonAsync<List<Class1>>();
I recommend you to use more meaningful name instead of Class1 you can name it Apod (acronym for Astronomy Picture of the Day)
Full working code:
using System.Text.Json;
using System.Text.Json.Serialization;
HttpClient client = new HttpClient();
const string BaseUrl = #"https://api.nasa.gov/";
var response = await client.GetAsync($"{BaseUrl}planetary/apod?start_date=2022-03-01&end_date=2022-03-08&api_key=DEMO_KEY");
if ((response.StatusCode != System.Net.HttpStatusCode.OK))
{
Console.Error.WriteLine("field to fetch data from server");
}
var responseBody = await response.Content.ReadAsStringAsync();
var pictuersList = JsonSerializer.Deserialize<List<Apod>>(responseBody);
Console.WriteLine($"there is {pictuersList?.Count} apod downloaded successflly");
Console.WriteLine("done");
public class Apod
{
[JsonPropertyName("copyright")]
public string Copyright { get; set; } = "";
[JsonPropertyName("date")]
public string Date { get; set; } = "";
[JsonPropertyName("explanation")]
public string Explanation { get; set; } = "";
[JsonPropertyName("hdurl")]
public string Hdurl { get; set; } = "";
[JsonPropertyName("media_type")]
public string MediaType { get; set; } = "";
[JsonPropertyName("service_version")]
public string ServiceVersion { get; set; } = "";
[JsonPropertyName("title")]
public string Title { get; set; } = "";
[JsonPropertyName("url")]
public string Url { get; set; } = "";
}
The object your JSON containing is not some container with the array in it, it IS the array. So, the correct code would be like this:
var responseObject = await response.Content.ReadFromJsonAsync<Class1[]>();
The correct JSON for your code would look like this:
{
"Property1": [{
"copyright": "Jeff DaiTWAN",
"date": "2022-03-01",
"url": "https://apod.nasa.gov/apod/image/2203/DuelingBands_Dai_960.jpg"
}]
}

Deserializing a JSON to c# object when key contains dollar($)(Metadata)

So i'm getting a response like this
{"$id":"1","success":true,"errors":{"$id":"2","$values":[]}}
how can i convert this into to a c# object, tried using this(http://json2csharp.com/) tool to make an output but it doesn't make sense
this is what i'm getting
x
public class Errors
{
public string __invalid_name__$id { get; set; }
public List<object> __invalid_name__$values { get; set; }
}
public class RootObject
{
public string __invalid_name__$id { get; set; }
public bool success { get; set; }
public Errors errors { get; set; }
}
I'm kinda new to c#, any inputs would be deeply appreciated, i basically need access to success key variable
You need to add [JsonProperty] attribute to every property that key name started with dollar $
public class Errors
{
[JsonProperty("$id")]
public string id { get; set; }
[JsonProperty("$values")]
public List<object> values { get; set; }
}
public class RootObject
{
[JsonProperty("$id")]
public string id { get; set; }
public bool success { get; set; }
public Errors errors { get; set; }
}
Because the $ indicates metadata, not an actual data field. so you have to modify your JsonSerializerSettings to ignore MetadataPropertyHandling.
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
And finally deserialize your json to above class objects.
RootObject rootObject = JsonConvert.DeserializeObject<RootObject>(json, settings);
Here I created a sample console app for demonstration purpose that shows how above code will work.
class program
{
public static void Main()
{
string json = File.ReadAllText(#"Path to your json file");
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
RootObject rootObject = JsonConvert.DeserializeObject<RootObject>(json, settings);
Console.WriteLine("id: " + rootObject.id);
Console.WriteLine("success: " + rootObject.success);
Console.WriteLine("errors.id: " + rootObject.errors.id);
Console.WriteLine("errors.values: " + string.Join(",", rootObject.errors.values));
Console.ReadLine();
}
}
Output:
Well, What you can do is
public class Errors
{
[JsonProperty(PropertyName = "$id")]
public string id { get; set; }
[JsonProperty(PropertyName = "$values")]
public List<object> values { get; set; }
}
public class RootObject
{
[JsonProperty(PropertyName = "$id")]
public string id { get; set; }
public bool success { get; set; }
public Errors errors { get; set; }
}
You need your object attributes to match you json string ($id instead of _invalid_name_$id), then you can use:
JsonConvert.DeserializeObject<RootObject>(jsonString);
Here is a simple class to serialize json string from object or to object (T). May de/serialize array(list) of objects.
public class HelperSerializer<T> where T: class
{
public static string WriteFromObject(T source)
{
using (var ms = new MemoryStream()) {
var ser = new DataContractJsonSerializer(typeof(T));
ser.WriteObject(ms, source);
byte[] json = ms.ToArray();
return Encoding.UTF8.GetString(json, 0, json.Length);
}
}
// Deserialize a JSON stream to an object.
public static T ReadToObject(string json)
{
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
var ser = new DataContractJsonSerializer(typeof(T));
return ser.ReadObject(ms) as T;
}
}
}
Use persons = HelperSerializer<List<Person>>.ReadToObject(json);
and var json = HelperSerializer<List<Person>>.WriteFromObject(persons);

JSON to object C# (mapping complex API response to C# object)

I am able to handle simple JSON serialization and deserialization but this API response seems little complicated, and I am seeking an advice as to what would be ideal approach to tackle this.
I'm trying to call an API for MVC application.
Goal is to map API data to model.
API endpoint is
https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=1min&apikey=MyAPIKey
Troubles here are:
JSON data keys have white space in them.
When I tried doing paste special in Visual studio, It gave me a long
list of classes for each date entry separately, because this API
call returns a separate set of information for date.
To solve problem explained in point 1, I used [JsonProperty("1. Information")] in class. And in my code..
public async Task TSI()
{
HttpClient client = new HttpClient();
//Uri uri = new Uri("http://date.jsontest.com/");
Uri uri = new Uri("https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=5min&apikey=demo");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
dynamic result = await response.Content.ReadAsAsync<object>();
IEnumerable<dynamic> dObj = JsonConvert.DeserializeObject<dynamic>(result.ToString());
IEnumerable<dynamic> t1 = dObj.FirstOrDefault();
IEnumerable<dynamic> t2 = dObj.LastOrDefault();
dynamic MetaData = t1.FirstOrDefault();
Rootobject ro = new Rootobject();
ro.MetaData = MetaData;
}
PS: I'm relatively new to make API calls and handling them.
I was able to make a call to
date.jsontest.com
and map the API data to model (which I had created using paste special)
//API response
{
"time": "12:53:22 PM",
"milliseconds_since_epoch": 1504875202754,
"date": "09-08-2017"
}
//C# code to map to API data
public class sampleObject
{
public string time { get; set; }
public long milliseconds_since_epoch { get; set; }
public string date { get; set; }
}
My RootObject looks like this:
public class Rootobject
{
[JsonProperty("Meta Data")]
public MetaData MetaData { get; set; }
[JsonProperty("Time Series (1min)")]
public TimeSeries1Min TimeSeries1min { get; set; }
}
public class MetaData
{
[JsonProperty("1. Information")]
public string _1Information { get; set; }
[JsonProperty("2. Symbol")]
public string _2Symbol { get; set; }
[JsonProperty("3. Last Refreshed")]
public string _3LastRefreshed { get; set; }
[JsonProperty("4. Interval")]
public string _4Interval { get; set; }
[JsonProperty("5. Output Size")]
public string _5OutputSize { get; set; }
[JsonProperty("6. Time Zone")]
public string _6TimeZone { get; set; }
}
// I have so many of these sub-classes for dates, which again is an issue
public class TimeSeries1Min
{
public _20170907160000 _20170907160000 { get; set; }
public _20170907155900 _20170907155900 { get; set; }
....
....}
public class _20170907160000
{
public string _1open { get; set; }
public string _2high { get; set; }
public string _3low { get; set; }
public string _4close { get; set; }
public string _5volume { get; set; }
}
public class _20170907155900
{
public string _1open { get; set; }
public string _2high { get; set; }
public string _3low { get; set; }
public string _4close { get; set; }
public string _5volume { get; set; }
}
It is hard to create a model from this json, but you can convert those data to dictionary
var jObj = JObject.Parse(json);
var metadata = jObj["Meta Data"].ToObject<Dictionary<string, string>>();
var timeseries = jObj["Time Series (1min)"].ToObject<Dictionary<string, Dictionary<string, string>>>();
The following code should do what you want
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
var obj = JsonConvert.DeserializeObject<Rootobject>(result);
//No idea what you want to do with this line as there is no MetaData property on the root object
obj.MetaData = MetaData;
}

How do I create a Dictionary<string,string> for nested object

I am retrieving the following JSON via a POST to an API
{
"State":"Andhra_Pradesh",
"District":"Guntur",
"Fact":"SELECT",
"Description":"",
"FactDate":"",
"FactNumber":"",
"FactType":"SELECT",
"Fact":{"Id":"1"}
}
I am able to execute the Ajax request via javascript, but I also want to consume the API through C# code.
I am using the below code, but I'm not quite sure on how to add the Fact object?
var values = new Dictionary<string, string>
{
{ "State", selectedState },
{ "District", selectedDistrict },
{ "Fact", ""},
{ "FactType", ""},
{ "FactNumber", ""},
{ "Description", ""},
{"Fact", "{Id,1}" },
{"FactDate", factDate.Date.ToString() }
};
using (var httpClient = new HttpClient())
{
var content = new FormUrlEncodedContent(values);
var response = await httpClient.PostAsync("http://api.in/" + "test", content);
}
How do I add the Fact object to Dictionary?
You'll probably need to define the data you are sending as actual class before using httpclient.
If you had only name value pairs then you could have used the NameValueCollection and sent as a formurlencoded but since you have a complex type, you might consider this below.
See below.
public class Rootobject
{
public string State { get; set; }
public string District { get; set; }
public Fact Fact { get; set; }
public string Description { get; set; }
public string CaseDate { get; set; }
public string FactNumber { get; set; }
public string FactType { get; set; }
}
public class Fact
{
public string Id { get; set; }
}
Usage is as below. be sure to include a reference to System.Net.Http.Formatting.dll
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var model = new Rootobject { State = "Andhra_Pradesh", District = "Guntur", FactType = "SELECT", Description = "", CaseDate = "", FactNumber = "", Fact = new Fact { Id = "1"} };
var data = await client.PostAsJsonAsync("http://api.in/" + "test", model);
I think this is just a json object, you can either create a class which have the same properties of (state, district etc ..) and use json serializer
or you can create JObject using Json.Net
You can use Newtonsonft.Json to to the serializaton/deserialization job and the code will be like that.
public class Rootobject
{
[JsonProperty("State")]
public string State { get; set; }
[JsonProperty("District")]
public string District { get; set; }
[JsonProperty("Fact")]
public Fact Fact { get; set; }
[JsonProperty("Description")]
public string Description { get; set; }
[JsonProperty("CaseDate")]
public string CaseDate { get; set; }
[JsonProperty("FactNumber")]
public string FactNumber { get; set; }
[JsonProperty("FactType")]
public string FactType { get; set; }
}
public class Fact
{
[JsonProperty("Id")]
public string Id { get; set; }
}
And then, after instatiating your object, just serialize it.
Rootobject example = new Rootobject();
//Add values to the variable example.
var objectSerialized = JsonConvert.SerializeObject(example);
After that, you will have a json ready to be send wherever you want.
Just change {"Fact", "{Id,1}" } to {"Fact.Id", "1" },

Data at root level is invalid: Trying to deserialize json string

I'm using Asp.Net, and although I've done some work deseriaizing Xml before, I've not got to do the same with json.
The error I'm getting is Data at root level is invalid, something I've seen before with Xml deserialization.
Here's the documentation I have for the response:
HTTP ResponseCode: 200
HTTP Status: OK
HTTP Body:
{
“Status”:”Success”,
“Response”:”0”,
“Price”:”10.00”,
“BuyerId”:999,
“BuyerContractId”:9999,
“Detail”:”https://...”
}
I'm using a WebClient to get the data back:
response = wc.UploadString(info.Endpoint, info.Data);
"response" is a string. I use this method to deserialize:
public static T JsonResponse<T>(string response)
where T : class
{
var s = new DataContractJsonSerializer(typeof(T));
using (var r = XmlReader.Create(new StringReader(response)))
{
return (T)s.ReadObject(r);
}
}
The class I'm trying to deserialize to is:
[DataContract]
public class ResponseProps
{
[DataMember(Name = "Status")]
public string Status { get; set; }
[DataMember(Name = "Response")]
public string Response { get; set; }
[DataMember(Name = "Price")]
public decimal Price { get; set; }
[DataMember(Name = "BuyerId")]
public string BuyerId { get; set; }
[DataMember(Name = "BuyerContractId")]
public string BuyerContractId { get; set; }
[DataMember(Name = "Detail")]
public string Detail { get; set; }
}
Here's how it's called:
var cr = XmlHelper.JsonResponse<ResponseProps>(response);
Anyone got any clues as to where I'm going wrong?
Assuming the data comes in JSON format, I changed the following -
public static T JsonResponse<T>(string response)
where T : class
{
return JsonConvert.DeserializeObject<T>(response);
}
Now this works fine-
var q = JsonResponse<ResponseProps>('jsonString');

Categories