Accessing a JSON object inside HttpResponseMessge using StringContent - c#

I have a problem accessing a JSON object inside a HttpResponseMessage that is made using StringContent.
Here's what the JSON Objects looks like:
{
"object": "JOB",
"entry": [
{
"uuid": "1nufisnfiu3-dn3u2irb-dn3ui2fb9-nui2",
"changed_fields": [
"status"
],
"time": "2018-09-30 21:57:02"
}
],
"resource_url": "https://somewebsiteAPI.com/api_1.0/Jobs/1nufisnfiu3-dn3u2irb-dn3ui2fb9-nui2.json"
}
Here's my Controller:
[HttpPost]
public async Task<HttpResponseMessage> Post()
{
string result = await Request.Content.ReadAsStringAsync();
var resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new StringContent(result, System.Text.Encoding.UTF8, "application/json");
return resp;
}
My objective here is to get the resource_url json object.
Any help would be so much appreciated! Thanks!

You need to let JSON string to be object, then get resource_url value.
I would use Json.net to make it.
Create your model for Json data.
look like this.
public class Entry
{
public string uuid { get; set; }
public List<string> changed_fields { get; set; }
public string time { get; set; }
}
public class RootObject
{
public List<Entry> entry { get; set; }
public string resource_url { get; set; }
}
use JsonConvert.DeserializeObject method let JSON string to model.
then get the model resource_url property and pass as parameter in StringContent.
[HttpPost]
public async Task<HttpResponseMessage> Post()
{
string result = await Request.Content.ReadAsStringAsync();
var resp = new HttpResponseMessage(HttpStatusCode.OK);
var model = JsonConvert.DeserializeObject<RootObject>(result);
resp.Content = new StringContent(model.resource_url, System.Text.Encoding.UTF8, "application/json");
return resp;
}

Should really let the model binder do its intended roles instead of trying to parse the JSON yourself.
[HttpPost]
public async Task<IHttpActionResult> Post([FromBody]Job model) {
if(ModelState.IsValid) {
//...do something with model
return Ok(model.ResourceUrl);
}
return BadRequest(ModelState);
}
Where Job is defined from the provided JSON
public partial class Job {
[JsonProperty("object")]
public string Object { get; set; }
[JsonProperty("entry")]
public Entry[] Entry { get; set; }
[JsonProperty("resource_url")]
public Uri ResourceUrl { get; set; }
}
public partial class Entry {
[JsonProperty("uuid")]
public string Uuid { get; set; }
[JsonProperty("changed_fields")]
public string[] ChangedFields { get; set; }
[JsonProperty("time")]
public DateTimeOffset Time { get; set; }
}
The model binder would parse the incoming JSON and populate the strongly typed object model defined to match the desired data.

Related

How to structure my models to match my JSON?

I'm just new to API and I wanted to consume third party API that was provided to me(which I don't have any control on how they structure the datas). I have asked on S.O before this post but I can't understand what or how should I do their suggestions 'Structure your classes to match your JSON Object'
My Json looks like this :
{
"status": 1,
"message": "",
"data": {
"VacationLeave": 11,
"SickLeave": 10,
"EmergencyLeave": 2,
"HolidaySwap": 1,
"OldLeave": 1
}
}
And on my controllers I have this
public IActionResult APICall()
{
// Fetch the JSON string from URL.
List<APIResponse> leaves = new List<APIResponse>();
string apiUrl = "http://xxxxx.xxx.xxx/GetLeaveBalance/2170";
HttpClient client = new HttpClient();
HttpResponseMessage response = client.GetAsync(apiUrl).Result;
if (response.IsSuccessStatusCode)
{
leaves = JsonConvert.DeserializeObject<List<APIResponse>>(response.Content.ReadAsStringAsync().Result);
}
// Return the Deserialized JSON object.
return Json(leaves);
}
My model classes :
public class APIResponse : LeaveModel
{
public int Status { get; set; }
public string Message { get; set; }
public List<LeaveModel> Leaves;
}
public class LeaveModel
{
public int VacationLeave { get; set; }
public int SickLeave { get; set; }
public int EmergencyLeave { get; set; }
public int HolidaySwap { get; set; }
public int OldSwap { get; set; }
}
My problem is: I get this error :
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[APITest.Models.APIResponse]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
Can anyone help me what I'm missing here?
Thank you
Your APIResponse should support generic.
public class APIResponse<T>
{
public int Status { get; set; }
public string Message { get; set; }
public T Data { get; set; }
}
As the data is a LeaveModel object but not a LeaveModel array.
And deserialize as APIResponse<LeaveModel> type.
APIResponse<LeaveModel> apiResponse = JsonConvert.DeserializeObject<APIResponse<LeaveModel>>(response.Content.ReadAsStringAsync().Result);
LeaveModel leave = apiResponse.Data;
And would suggest changing the method to asynchronous.
public async Task<IActionResult> APICallAsync()
{
//Fetch the JSON string from URL.
LeaveModel leave = new LeaveModel();
string apiUrl = "http://xxxxx.xxx.xxx/GetLeaveBalance/2170";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(apiUrl);
if (response.IsSuccessStatusCode)
{
APIResponse<LeaveModel> apiResponse = JsonConvert.DeserializeObject<APIResponse<LeaveModel>>(await response.Content.ReadAsStringAsync());
leave = apiResponse.Data;
}
//Return the Deserialized JSON object.
return Json(leave);
}
As per json ApiResponse class must look like this
public class APIResponse : LeaveModel
{
public int Status { get; set; }
public string Message { get; set; }
public LeaveModel data;
}

class property manipulation when parsing json data in c#

I am practicing with web api. My goal is to create a Get endpoint, which receive data from an external api, then return a different result. external api link: https://www.themealdb.com/api/json/v1/1/search.php?f=a, The external api data looks like:
{
"meals": [
{
"idMeal": "52768",
"strMeal": "Apple Frangipan Tart",
"strDrinkAlternate": null,
"strCategory": "Dessert",
.....
},
{
"idMeal": "52893",
"strMeal": "Apple & Blackberry Crumble",
....
}
]
}
I want my endpoint provide a different result like the following:
[
{
"idMeal": "52768",
"strMeal": "Apple Frangipan Tart",
"ingredients": ["Apple", "sugar"...]
},
{
"idMeal": "52893",
"strMeal": "Apple & Blackberry Crumble",
"ingredients": ["Apple", "sugar"...]
}
]
The following code is what I attempted so far, It's working, but the moment I changed property ingredient1 from public to private, that ingredient in list will become null, also, there are so many ingredients, some of them are null by default, I don't want to add them if they are null, how can I fix these two issues? Thanks a lot
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;
using RestSharp;
namespace testAPI.Controllers;
public class Content
{
[JsonPropertyName("meals")]
public List<Meal> Meals { get; set; }
}
public class Meal
{
[JsonPropertyName("idMeal")]
public string MealId { get; set; }
[JsonPropertyName("strMeal")]
public string Name { get; set; }
[JsonPropertyName("strIngredient1")]
public string Ingredient1 { get; set; }
[JsonPropertyName("strIngredient2")]
public string Ingredient2 { get; set; }
[JsonPropertyName("strIngredient20")]
public string Ingredient20 { get; set; }
public List<string> Ingredients
{
get { return new List<string>(){Ingredient1, Ingredient2, Ingredient20};}
}
}
[ApiController]
[Route("api/[controller]")]
public class DishesController : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetAllRecipes()
{
var client = new RestClient($"https://www.themealdb.com/api/json/v1/1/search.php?s=");
var request = new RestRequest();
var response = await client.ExecuteAsync(request);
var mealList = JsonSerializer.Deserialize<Content>(response.Content);
return Ok(mealList.Meals);
}
}
To address the problems one at a time...
the moment I changed property ingredient1 from public to private, that ingredient in list will become null
Changing the access modifier affects both deserialization and serialization, so this cannot be used to only stop it from serializing the property. You should split the data models up into what you want to receive and what you want to expose/return.
there are so many ingredients, some of them are null by default, I don't want to add them if they are null
Addition to splitting up the data models you can handle this when mapping from one model to the other.
The following code should fix both issues:
namespace TheMealDb.Models
{
// These are the models you receive from TheMealDb
// JSON converted to classes with https://json2csharp.com/
public class Root
{
public List<Meal> meals { get; set; }
}
public class Meal
{
public string idMeal { get; set; }
public string strMeal { get; set; }
public string strIngredient1 { get; set; }
public string strIngredient2 { get; set; }
public string strIngredient3 { get; set; }
// Other properties removed for brevity...
}
}
namespace Internal.Models
{
// This is the model you want to return from your controller action
public class Meal
{
[JsonPropertyName("id")] // No need to use the same name as from themealdb
public string Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("ingredients")]
public List<string> Ingredients { get; set; }
}
}
Now, to fetch, map and return the data in your controller action:
[HttpGet]
public async Task<IActionResult> GetAllRecipes()
{
var client = new RestClient($"https://www.themealdb.com/api/json/v1/1/search.php?s=");
var request = new RestRequest();
var response = await client.ExecuteAsync(request);
// Deserialize to the "TheMealDb" models
var mealList = JsonSerializer.Deserialize<TheMealDb.Models.Root>(response.Content);
// Map to your own models
var myMealList = mealDbList.meals?.Select(MapToInternal);
return Ok(myMealList);
}
// Map "TheMealDb" model to your own model
private Internal.Models.Meal MapToInternal(TheMealDb.Models.Meal externalMeal)
{
return new Internal.Models.Meal
{
Id = externalMeal.idMeal,
Name = externalMeal.strMeal,
Ingredients = new []
{
externalMeal.strIngredient1,
externalMeal.strIngredient2,
externalMeal.strIngredient3,
// ...
}
// Remove empty/null ingredients
.Where(ingr => !string.IsNullOrEmpty(ingr))
.ToList()
};
}
See the code in action.

Call and get response from web api in a winform c# application

I am making a simple WinForm Application in Windows and I want to get some data about foreign exchange rates. So I decided to call an API from Oanda. I tried several things around but nothing worked. It gives the response in CSV as well as JSON format. I don't know which will be easier to handle.
Also for this type of response, I am unable to create its model class.
Response:
JSON:
{
"meta": {
"effective_params": {
"data_set": "OANDA",
"base_currencies": [
"EUR"
],
"quote_currencies": [
"USD"
]
},
"endpoint": "spot",
"request_time": "2019-06-08T12:05:23+00:00",
"skipped_currency_pairs": []
},
"quotes": [
{
"base_currency": "EUR",
"quote_currency": "USD",
"bid": "1.13287",
"ask": "1.13384",
"midpoint": "1.13336"
}
]
}
CSV:
base_currency,quote_currency,bid,ask,midpoint
EUR,USD,1.13287,1.13384,1.13336
I just need those three numbers so, which method will be helpful and how.
This code I already tried:
var client = new HttpClient();
client.BaseAddress = new Uri("https://www1.oanda.com/rates/api/v2/rates/");
HttpResponseMessage response = await client.GetAsync("spot.csv?api_key=<myapikey>&base=EUR&quote=USD");
string result = await response.Content.ReadAsStringAsync();
textBox1.Text = result;
Edit: I need the result of this call for my further processing so I must need this method to complete its execution before proceeding further
First creating model from Json:
use a online model generator like Json2C#, for the Json that you have posted, following is the model generated:
public class EffectiveParams
{
public string data_set { get; set; }
public List<string> base_currencies { get; set; }
public List<string> quote_currencies { get; set; }
}
public class Meta
{
public EffectiveParams effective_params { get; set; }
public string endpoint { get; set; }
public DateTime request_time { get; set; }
public List<object> skipped_currency_pairs { get; set; }
}
public class Quote
{
public string base_currency { get; set; }
public string quote_currency { get; set; }
public string bid { get; set; }
public string ask { get; set; }
public string midpoint { get; set; }
}
public class RootObject
{
public Meta meta { get; set; }
public List<Quote> quotes { get; set; }
}
Now connecting to the WebAPI using HttpClient, which has the option to return both Json and CSV, I would prefer JSON being standard, which can also be consumed easily by variety of clients, use the following simple generic methods:
Assuming it is GET only call, just supply the Host and API details to the generic Process method underneath:
public async Task<TResponse> Process<TResponse>(string host,string api)
{
// Execute Api call Async
var httpResponseMessage = await MakeApiCall(host,api);
// Process Json string result to fetch final deserialized model
return await FetchResult<TResponse>(httpResponseMessage);
}
public async Task<HttpResponseMessage> MakeApiCall(string host,string api)
{
// Create HttpClient
var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) { BaseAddress = new Uri(host) };
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Make an API call and receive HttpResponseMessage
HttpResponseMessage responseMessage = await client.GetAsync(api, HttpCompletionOption.ResponseContentRead);
return responseMessage;
}
public async Task<T> FetchResult<T>(HttpResponseMessage result)
{
if (result.IsSuccessStatusCode)
{
// Convert the HttpResponseMessage to string
var resultArray = await result.Content.ReadAsStringAsync();
// Json.Net Deserialization
var final = JsonConvert.DeserializeObject<T>(resultArray);
return final;
}
return default(T);
}
How to use:
Simply call:
var rootObj = await Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/");
You receive the deserialized RootObject as shown in the model above
For anything further complex processing like sending input to the call with http body, above generic code needs further modification, it is currently only specific to your requirement
Edit 1: (Making the entry call Synchronous)
To make the overall call synchronous, use the GetAwaiter().GetResult() at the topmost level, Main method will be converted to, rest all will remain same as in the sample (async methods)
void Main()
{
var rootObj = Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/").GetAwaiter().GetResult();
}
Edit 2: (Making complete code Synchronous)
void Main()
{
var rootObj = Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/");
}
public TResponse Process<TResponse>(string host, string api)
{
// Execute Api call
var httpResponseMessage = MakeApiCall(host, api);
// Process Json string result to fetch final deserialized model
return FetchResult<TResponse>(httpResponseMessage);
}
public HttpResponseMessage MakeApiCall(string host, string api)
{
// Create HttpClient
var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) { BaseAddress = new Uri(host) };
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Make an API call and receive HttpResponseMessage
HttpResponseMessage responseMessage = client.GetAsync(api, HttpCompletionOption.ResponseContentRead).GetAwaiter().GetResult();
return responseMessage;
}
public T FetchResult<T>(HttpResponseMessage result)
{
if (result.IsSuccessStatusCode)
{
// Convert the HttpResponseMessage to string
var resultArray = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
// Json.Net Deserialization
var final = JsonConvert.DeserializeObject<T>(resultArray);
return final;
}
return default(T);
}
You can use an online service such as json2csharp to get your json model and Json.Net to serialise and deserialise the json string. The following models your json.
public class EffectiveParams
{
public string data_set { get; set; }
public List<string> base_currencies { get; set; }
public List<string> quote_currencies { get; set; }
}
public class Meta
{
public EffectiveParams effective_params { get; set; }
public string endpoint { get; set; }
public DateTime request_time { get; set; }
public List<object> skipped_currency_pairs { get; set; }
}
public class Quote
{
public string base_currency { get; set; }
public string quote_currency { get; set; }
public string bid { get; set; }
public string ask { get; set; }
public string midpoint { get; set; }
}
public class RootObject
{
public Meta meta { get; set; }
public List<Quote> quotes { get; set; }
}
Note that you can change RootOject to a more descriptive name.
So, for example, to get the bid, ask and midpoint value for each quotes, you can simply do this:
RootObject rootObj=JsonConvert.DeserializeObject(jsonString);
//Get the required values.
foreach(var quote in rootObj.quotes)
{
Console.WriteLine($"Bid : {quote.bid} Ask: {quote.ask} MidPoint: {quote.midpoint}");
}

webapi not recognize json list

I've got a list of objects in JSON that isn't recognized by a WebApi2 controller
The JSON list is the following:
{
"FirstObjectType": [{"Name": "the_name"}],
"SecondObjectType": [{"Label": "01_obj"}, {"Label": "02_obj"}]
}
The Model class is:
public class CompositeObject
{
[JsonProperty("FirstObjectType")]
public List<FirstObject> fo { get; set; }
[JsonProperty("SecondObjectType")]
public List<SecondObject> so { get; set; }
}
The controller is:
public IHttpActionResult PostList([FromBody] CompositeObject jsonList)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
List<FirstObject> fo_list = jsonList.fo;
foreach (var item in fo_list)
{
db.FirstObject.Add(item);
db.SaveChanges();
}
return StatusCode(HttpStatusCode.OK);
}
When I submit the Post action, the controller recognize both lists in CompositeObject jsonList as Null
There is a problem in your model, where names are not being matched. You have to update model as:
public class FirstObjectType
{
public string Name { get; set; }
}
public class SecondObjectType
{
public string Label { get; set; }
}
public class RootObject
{
public List<FirstObjectType> FirstObjectType { get; set; }
public List<SecondObjectType> SecondObjectType { get; set; }
}
I have assumed that FirstObjectType contains string with name Name and SecondObjectType contains string with name Label. Make sure to use same names for properties of FirstObjectType and SecondObjectType class as in JSON string.
The issue was in the client code because I missed to set the Content-type as application/json in the header section.
In this way the WebApi server doesn't recognize in the right way the JSON object (I think that the server look for a x-www-form-urlencoded type)
So, the code above is right, but I have found another solution
In the WebApi controller:
using Newtonsoft.Json.Linq;
public IHttpActionResult PostList([FromBody] JObject ReceivedObjectsList)
{
var receivedLists = ReceivedObjectsList.Properties();
List<FirstObject> fo = ReceivedObjectsList["FirstObjectType"].ToObject<List<FirstObject>>();
List<SecondObject> so = ReceivedObjectsList["SecondObjectType"].ToObject<List<SecondObject>>();
...
}

Filling list with Json data and getting the objects from it

I have a php webservice which gets the data from database and returns it as json data.
Json data
{"faqs":
[
{"faq":{"id":"123"}},
{"faq":{"id":"124"}}
]
}
Object classes
public class FaqList
{
public List<Faq> faqs { get; set; }
}
public class Faq
{
public string id { get; set; }
}
Test class
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(new Uri("http://www.mydomain.com/webservice/7/server.php"));
var jsonString = await response.Content.ReadAsStringAsync();
FaqList list = JsonConvert.DeserializeObject<FaqList>(jsonString);
list.faqs.Count() => 2
list.faqs[0].id => NULL !!
I fill all the objects to the 'list'. With count test I see that it's filled. But if I try to get an objects from it, I get null error.
So, how can I correctly fill my list so that I can get the objects from it?
http://json2csharp.com/ suggests these definitions. (One more class Faq2)
public class Faq2
{
public string id { get; set; }
}
public class Faq
{
public Faq2 faq { get; set; }
}
public class RootObject
{
public List<Faq> faqs { get; set; }
}

Categories

Resources