C# - Newtonsoft : Generic class for Client Response - c#

After hours of attempts and research, I am asking for your help.
I am calling a public API which returns the same structure except for the datas returned.
For examples, the REST calls which retrieve stations and districts return those two JSON answers :
Stations response :
"response" : {
"status": { "#attributes": {"code": "0", "message": "OK"} },
"data" : {
"station": [{
"number": "stationId",
"name": "stationName",
"address": "stationAddress",
"state": "1",
"latitude": "stationLat",
"longitude": "stationLong",
"slotsavailable": "10",
"bikesavailable": "20",
"pos": "0",
"district": "stationDistrict",
"lastupdate": "2016-03-28T11:47:08+02:00"
}, {...}, ...]}
}
Districts response :
"response" : {
"status": { "#attributes": {"code": "0", "message": "OK"} },
"data" : { "district": [{"id": "districtId", "name": "districtName"}, {...}, ...] }
}
I am using a .NET 4.5/C# solution with Newtonsoft.Json to execute the call.
I want to make the object, mapped to the client response, generic so the execution of the call will be made as follow :
var result = await client.Execute<Response<ApiResponseDistrict>>(request);
var result = await client.Execute<Response<ApiResponseStation>>(request);
My first attempt was to make a non generic call (create a full object by returned datas) which was a success.
My second attempt was to created a generic object so I made the following classes using the JsonProperty of the library Newtonsoft :
public class ApiResponse<T>
{
[JsonProperty("response")]
public Response<T> Response { get; set; }
}
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public Data<T> Data { get; set; }
}
public class Data<T>
{
public T ResponseData { get; set; }
}
public class ApiResponseDistrict
{
[JsonProperty("district")]
public List<District> Districts { get; set; }
}
public class District
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
At this point, when I am executing the call the object Response is valorized and also its property Status with the value waited but the property Data is never valorized (null).
My third attempt was to continue on the second attempt but using the JsonObject of the Newtonsoft library which it's given (with the same result) :
[JsonObject("district")]
public class ApiResponseDistrict
{
public List<District> Districts { get; set; }
}
As I am new to Newtonsoft, I would like to know if it is possible to use generic classes, as I am trying to do, to mapped the object returned by the call or I have to create a complete object for each "data" returned ?
Thank you for your answer and explanations or clues for me to find the answer !

public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public Data<T> Data { get; set; }
}
public class Data<T>
{
public T ResponseData { get; set; }
}
This adds another layer between the data, so a response would look like this:
{
"Status": …,
"Data": {
"ResponseData": {
<The actual type T>
}
}
}
Instead, you want to remove that ResponseData level:
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
}
So for example, for the JSON above, you would have a StationResponseData class:
public class StationResponseData
{
public List<Station> Stations
{ get; set; }
}
And then you would deserialize the JSON as Response<StationResponseData>. The Station class would then contain those properties for number, name, address, etc.

Related

How to deserialize a JSON

I am attempt to use an API that use the follow example structure for their returned json:
[{
"name":"Firs",
"id":"0",
"data": {"scale":"1","size":"500"}
},
{
"name":"Second",
"id":"1",
"data":"222"
}]
Upade "[ ]"
But I can not figure out how to get it to be happy with the provided structure.
Using Newtonsoft I am able to Serialize and Deserialize a model shown below:
List<RequestJson> requestJson = JsonConvert.DeserializeObject<List<RequestJson>>(json);
public class GetData
{
public int Scale { get; set; }
public int Size { get; set; }
}
public class RequestJson
{
public string Name { get; set; }
//Problem is here
public GetData Data { get; set; }
}
Your JSON is not valid. The Data property does not match the object type.
Correct JSON:
[{
"name":"Firs",
"id":"0",
"data": {"scale":"1","size":"500"}
},
{
"name":"Second",
"id":"1",
"data": {"scale":"2","size":"600"}
}]

How to create a .NET Core Serializer for indented Javascript objects

I am trying to serialize JSON objects received from an API in a cli app. I'm having issues understanding how to create the objects in .NET for JSON objects which have an indented structure.
For example, this is fine:
{"status": "ok" }
public class Success
{
public string status { get; set; }
}
But something like this is where I'm stuck and both of the examples from the below return null when the client API receives them.
[
{
"id": "some_uuid_string_1",
"message": "hello"
},
{
"id": "some_uuid_string_2",
"message": "world"
}
]
Attempted solution
public class Received
{
public Dictionary<string,string> received { get; set; }
}
Alternatively I also tried a simpler structure, leaving out the explicit names and just using the IDs and values, which is closer to what my app requires and lets me make smaller requests.
{
"some_uuid_string_1": "hello",
"some_uuid_string_2": "world"
}
For this example I tried this, a list of key value pairs in the form of a dictionary.
public class Message
{
public Dictionary<string,string> message { get; set; }
}
public class Received
{
public List<Message> received { get; set; }
}
How can I create objects in C# for these two structures? One indented with set names and one 'generic' with no set names.
public class MyClass
{
public string id { get; set; }
public string message { get; set; }
}
[
{
"id": "some_uuid_string",
"message": "hello"
},
{
"id": "some_uuid_string",
"message": "world"
}
]
Deserializes to a List<MyClass> or MyClass[]
{
"some_uuid_string_1": "hello",
"some_uuid_string_2": "world"
}
Deserializes to
public class MyClass
{
public string some_uuid_string_1 { get; set; }
public string some_uuid_string_2 { get; set; }
}
or Dictionary<string, string>
The reason your Received class solution didn't work is because it is expecting a JSON property of received, as your class has a property named received, but the JSON does not.
This is the same issue with your Message class. Your class has property message whereas your JSON does not.
create a class
public class MessageId
{
public string id { get; set; }
public string message { get; set; }
}
you can deserialize your json
using Newtonsoft.Json;
var messages=JsonConvert.DeserializeObject<List<MessageId>>(yourJson);

Parse single values from HttpResponse

In making a call to the Google Geolocation API, the results (json) are returned like so
Example (Apologies, I couldn't get the Json to format properly)
{"geocoded_waypoints" : [
{
"geocoder_status" : "OK",
"place_id" : "EiQ3LCA3IExha2VzaWRlIERyLCBSeWUsIE5ZIDEwNTgwLCBVU0EiHRobChYKFAoSCQH00P0vl8KJEQ2d7mWAl0jrEgE3",
"types" : [ "subpremise" ]
},
{
"geocoder_status" : "OK",
"place_id" : "ChIJ1YqpR4eRwokRTuazxMrnKiM",
"types" : [ "establishment", "point_of_interest" ]
} ],
"routes" : [{
"bounds" : {
"northeast" : {
"lat" : 41.0044903,
"lng" : -73.6892836
},
"southwest" : {
"lat" : 40.9575099,
"lng" : -73.7589093
}
},
"copyrights" : "Map data ©2018 Google",
"legs" : [
{
"distance" : {
"text" : "7.0 mi",
"value" : 11325
},
"duration" : {
"text" : "15 mins",
"value" : 889
},
"end_address" : "851 Fenimore Rd, Mamaroneck, NY 10543, USA",
"end_location" : {
"lat" : 40.9575099,
"lng" : -73.75338219999999
},
"start_address" : "7 Pheasant Run #7, Rye, NY 10580, USA",
"start_location" : {
"lat" : 40.99850199999999,
"lng" : -73.689633
},
There are various single items I need to retrieve from the returned data, for example, the duration:text value.
Is there a way to filter out the excess in the API call or how do I parse this from the Json?
I tried deserializing to an object to then iterate over it and grab the legs array, but A. that doesn't work as the object is on big item, not a collection and B. that seems wasteful.
public int TravelTimeInMinutes(string origin, string destination, string apiKey)
{
var timeToTravel = 0;
var url = DirectionsUrlBase + ConvertOriginFormat(origin) + ConvertDestinationFormat(destination) + "&key="+ apiKey;
var client = new HttpClient();
// Add the Accept type header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
// get the response
// make method async once working
var response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
// Parse the response body.
var result = JsonConvert.SerializeObject(response.Content.ReadAsStringAsync().Result());
dynamic array = JsonConvert.DeserializeObject(result);
foreach (var item in array)
{
}
//… rest removed for brevity
How can I drill down to retrieve single values? I think my error is in how I am deserializing the response. I also have a Duration class with Text and Value properties. I would like to deserialize to that class if possible.
Follow up
I added .Result after the serialize call
response.Content.ReadAsStringAsync().Result()
this is now returning proper json
Now how can I parse single values from this or can I deserialize into the Duration class.
Per Charles suggestion
I have a root object
public class MapsDirectionObject
{
public GeocodedWaypoints[] geocoded_waypoints { get; set; }
public Routes[] routes { get; set; }
}
public class Routes
{
public object[] Value { get; set; }
}
public class GeocodedWaypoints
{
public string geocoder_status { get; set; }
public string place_id { get; set; }
public string[] types { get; set; }
}
And since the returned json only has two main children, routes and waypoints, I have those classes as well. The deserialization errors with the following error.
Newtonsoft.Json.JsonSerializationException: 'Error converting value "{ "geocoded_waypoints" : [
{
If I remove the .Result call on the serialization, I can map to that object but the values are null.
first of all I'd prefer we do strongly typed deserialization, so let's create classes corresponding to the data we need from the JSON:
public class ResultData
{
[JsonProperty("routes")]
public List<Route> Routes { get; set; }
}
public class Route
{
[JsonProperty("legs")]
public List<Leg> Legs { get; set; }
}
public class Leg
{
[JsonProperty("duration")]
public Duration Duration { get; set; }
}
public class Duration
{
[JsonProperty("text")]
public string Text { get; set; }
[JsonProperty("value")]
public int Value { get; set; }
}
then we deserialize the JSON:
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync("https://maps.googleapis.com/maps/api/directions/json?origin=7+7+Lakeside+Dr+Rye+NY&destination=Winged+Winged+Foot+Golf+Club").Result;
if (response.IsSuccessStatusCode)
{
var desed = JsonConvert.DeserializeObject<ResultData>(response.Content.ReadAsStringAsync().Result);
var durations = desed.Routes.SelectMany(r => r.Legs.Select(l => l.Duration)).ToList();
durations.ForEach(d => Console.WriteLine($"T: {d.Text}, V: {d.Value}"));
}
Note:
skip the serialization step ... ReadAsStringAsync() should produce a valid JSON string
First of all, please post a complete valid JSON object on your answer.
You uses Visual Studio for coding right? You can use Paste Special for help you:
1) Copy your entire JSON to clipboard;
2) Open your class file on Visual Studio;
3) Go to menu "Edit > Paste Special > Paste as JSON Class";
4) You will got something like this:
public class Rootobject
{
public Geocoded_Waypoints[] geocoded_waypoints { get; set; }
}
public class Geocoded_Waypoints
{
public string geocoder_status { get; set; }
public string place_id { get; set; }
public string[] types { get; set; }
}
Now you can deserialize to a typed object:
var myObject = JsonConvert.DeserializeObject<Rootobject>(result);
foreact(var geocoded_waypoints in myObject.geocoded_waypoints)
{
// do something with geocoded_waypoints
}
// your duration object:
var duration = myObject.routes[0].legs[0].duration;
If you want you can rename Rootobject to any name you want, like Geolocation.

Deserialize JSON with a list of two objects, one being a list of that object

I am trying to de-serialize a weird complex json string but having issues. I am getting an exception:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Response' because the type requires a JSON object (e.g. {"name":"value"})
The Json looks like this
{
"success":true,
"error":null,
"response":{
"responses":[
{
"success":true,
"error":null,
"response":{
"ob":{
"icon":"sunny.png",
"weatherShort":"Sunny"
}
},
"request":"requeststring"
},
{
"success":true,
"error":null,
"response":[
{
"indice":{
"current":{
"dateTimeISO":"2016-08-09T10:00:00-05:00",
"indexENG":"dry"
}
}
}
],
"request":"requeststring"
}
]
}
}
The problem when trying to create a C# class is that inside the responses list there is a Response object and a Response list.
Here is my class structure:
public class Responses
{
public bool success { get; set; }
public object error { get; set; }
public Response response { get; set; }
public List<Response> responses { get; set; }
public string request { get; set; }
}
public class Indice
{
public Current current { get; set; }
}
public class Current
{
public string indexENG { get; set; }
public string dateTimeISO { get; set; }
}
public class Ob
{
public string icon { get; set; }
public string weatherShort { get; set; }
}
public class Response
{
public List<Responses> responses { get; set; }
public Indice indice { get; set; }
public Ob ob { get; set; }
}
public class RootJsonObject
{
public bool success { get; set; }
public object error { get; set; }
public Response response { get; set; }
}
Am I doing something completely wrong here to handle the Responses list with a Response object and a Response list?
In case anyone wants to know, here is how I deserialize it:
RootJsonObject obj = JsonConvert.DeserializeObject<RootJsonObject>(response);
response being the string from a web request.
I am just trying to figure out how to map this strange JSON to a C# class. I've tried quite a few different class structures but seem to get the same exception regardless. I've also tried c# class generators but they don't give a decent output for this particular JSON. Appreciate any input! Thanks!
There is an error in your JSON. Second element in array has square brackets wrapping classic curly brackets, as if response was a collection but it's not. It's expected to be of type Response:
{
"success": true,
"error": null,
"response": [ <<<HERE {
"indice": {
"current": {
"dateTimeISO": "2016-08-09T10:00:00-05:00",
"indexENG": "dry"
}
}
}] <<<HERE,
"request": "requeststring"
}
Final, proper JSON that you should have received would look like this:
{
'success': true,
'error': null,
'response': {
'responses': [{
'success': true,
'error': null,
'response': {
'ob': {
'icon': 'sunny.png',
'weatherShort': 'Sunny'
}
},
'request': 'requeststring'
}, {
'success': true,
'error': null,
'response': {
'indice': {
'current': {
'dateTimeISO': '2016-08-09T10:00:00-05:00',
'indexENG': 'dry'
}
}
},
'request': 'requeststring'
}]
}
}

How to displayJson Object Name/Label while returning the list of the same object type

Here are my Class
[DataContract(Name="Test")]
public class Test
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Type { get; set; }
}
[DataContract(Name="Root")]
public static class Root
{
[DataMember(Name="TestList")]
public static List<Test> TestList { get; set; }
}
Expected Json To be returned
{
"Test":[
{
"Name": "MyApp",
"Type": "web"
},
{
"Name": "MyDatabase",
"Type": "db"
}
]
}
Actual Json Returned
[
{
"Name": "MyApp",
"Type": "web"
},
{
"Name": "MyDatabase",
"Type": "db"
}
]
WebApi Method to return the objects
[HttpGet]
public IEnumerable<Test> Get()
{
return Root.TestList;
}
The problem i am facing is when i run the above code I see the json data being returned in the "Actual" Format but i would love to see the Json in the "Expected Format" (please see above for the formats). The only difference is the label of the array. How can i put this label? i looked at tons of json docs but no luck. Please help.
Your method is returning a List<Test> so that will be serialized as a JSON array. If you want to see a JSON object with a named array-valued property, you need to return a POCO containing an appropriately named property, such as your Root:
[HttpGet]
public Root Get()
{
return Root;
}
Also, you need to change the name from TestList to Test:
[DataContract(Name="Root")]
public class Root
{
[DataMember(Name="Test")] // Changed this
public List<Test> TestList { get; set; }
}
Or, if your Root contains other properties you don't want serialized, or in other ways can't be serialized (because it's static), you can always return some generic wrapper, like so:
[DataContract]
public class RootWrapper<T>
{
[DataMember(Name = "Test")]
public T Test { get; set; }
}
And then
[HttpGet]
public RootWrapper<IEnumerable<Test>> Get()
{
return new RootWrapper<IEnumerable<Test>> { Test = Root.TestList };
}

Categories

Resources