In a WebAPI project, i have a controller that checks a status of a product, based on a value the user enters.
Lets say they enter "123" and the response should be "status": 1, AND a list of products. If they enter "321" the "status" is 0, AND a list of products.
My question is, how do i build such a string correct in a WebAPI controller.
[Route("{value:int}")]
public string GetProducts(int value)
{
var json = "";
var products = db.Products;
if (products.Any())
{
foreach (var s in products)
{
ProductApi product = new ProductApi();
product.Name = s.Name;
json += JsonConvert.SerializeObject(supplier);
}
}
var status = db.Status;
if (status.Any())
{
json += "{status:1}";
}
else
{
json += "{status:0}";
}
return json;
}
public class ProductApi
{
public string Name { get; set; }
}
Also, is this output/response considered valid?
[
{
"id":1,
"name":"product name"
},
{
"id":2,
"name":"product name 2"
},
{
"id":3,
"name":"product name 3"
}
]
{
"status": 0
}
So here are the changes for your post:
First, you should make your api return Json by default when you pass a text/html request (is this you are looking for?), adding this line to your WebApiConfig class:
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
Second, I changed the code to return a real object, impersonating your response:
public class ProductApiCollection
{
public ProductApi[] Products { get; set; }
public byte Status { get; set; }
}
public class ProductApi
{
public string Name { get; set; }
}
Method body:
public ProductApiCollection Get()
{
var result = new ProductApiCollection();
var dbProducts = db.Products;
var apiModels = dbProducts.Select(x => new ProductApi { Name = x.Name } ).ToArray();
result.Products = apiModels;
var status = db.Status.Any() ? 1 : 0;
result.Status = status;
return result;
}
This will results in the following example json:
{
"Products": [
{
"Name": "Pork"
},
{
"Name": "Beef"
},
{
"Name": "Chicken"
},
{
"Name": "Salad"
}
],
"Status": 1
}
I strongly advise you not to do manual formatting for such things, and rely on built-in and 3rd party libraries. Otherwise, you will be reinventing the things already available, tested and ready to work.
Just as raderick mentioned, you don't need to create your own custom JSON infrastructure.
public class ProductApi
{
public int Id {get;set;}
public string Name { get; set; }
}
public class ResponseDTO
{
public int Status {get;set;}
public List<ProductApi> { get; set; }
}
And in your API action, return like this:
[Route("{value:int}")]
public ResponseDTO GetProducts(int value)
{
ResponseDTO result = ...// construct response here
return result;
}
Related
I have a JSON being received from a public API that follows the structure below.
[
{
"ID": "12345",
"company": [
{
"contract_ID": "abc5678",
"company": [
{
"company_name": "HelloWorld",
"company_logo": "HW",
"invested": "2000"
}
]
},
{
"contract_ID": "67891",
"company": [
{
"company_name": "GoodBye",
"company_logo": "GB",
"invested": "500"
}
]
},
{
"contract_ID": "32658",
"company": [
{
"company_name": "YesNo",
"company_logo": "YN",
"invested": "1500"
}
]
}
]
}
]
I've tried several different methods of parsing, whether it be JTokens/JArrays or various classes. Something like the following allows me to access the first group of values (company_name, company_logo, invested), but I cannot iterate through to find the other two.
//receiving the json data
var responseString = await response.Content.ReadAsStringAsync();
var fullJSON = JsonConvert.DeserializeObject(responseString);
Debug.Log(fullJSON);
//trying to parse it down to just the data I need
JToken token = JToken.Parse("{\"root\":" + responseString + "}");
Debug.Log(token);
JArray assets = (JArray)token.SelectToken("root[0].assets");
Debug.Log(assets);
JToken dig = JToken.Parse("{\"root\":" + assets + "}");
Debug.Log(dig);
JArray assetsNested = (JArray)dig.SelectToken("root[0].assets");
Debug.Log(assetsNested);
My goal is to extract the contract_ID and then the associated company_name, company_logo, and invested items. For example, abc5678, HelloWorld, HW, and 2000 would be one of the three datasets needed.
The easiest way to deserialize this properly and keep your code maintainable is to use concrete classes. For example:
public sealed class Company
{
[JsonProperty("contract_ID")]
public string ContractID { get; set; }
[JsonProperty("company")]
public List<CompanyDefinition> CompanyDefinitions { get; set; }
}
public sealed class CompanyDefinition
{
[JsonProperty("company_name")]
public string CompanyName { get; set; }
[JsonProperty("company_logo")]
public string CompanyLogo { get; set; }
[JsonProperty("invested")]
public string Invested { get; set; }
}
public sealed class RootCompany
{
[JsonProperty("ID")]
public string ID { get; set; }
[JsonProperty("company")]
public List<Company> Company { get; set; }
}
Then, you simply deserialize. For example, if you are pulling from a file you could do this:
using(var sr = new StreamReader(#"c:\path\to\json.json"))
using(var jtr = new JsonTextReader(sr))
{
var result = new JsonSerializer().Deserialize<RootCompany[]>(jtr);
foreach(var r in result)
{
Console.WriteLine($"Company ID: {r.ID}");
foreach(var c in r.Company)
{
Console.WriteLine($"ContractID: {c.ContractID}");
foreach(var d in c.CompanyDefinitions)
{
Console.WriteLine($"Name: {d.CompanyName}");
Console.WriteLine($"Invested: {d.Invested}");
Console.WriteLine($"Company Logo: {d.CompanyLogo}");
}
}
}
}
When something changes with your models, you won't have to dig through lines upon lines of hard-to-read token selection code. You just update your models.
I have a situation while working with a JSON response from an API. To give a background, I am consuming an API from a source using a REST API using 3.5 .net framework.
Below is the part of the JSON output and I am kind of struggling to use it.
a)
"value": {
"Description": "Total Calculated things",
"2018": "5,820,456 ",
"2019": "2,957,447 "
}
The last 2 elements are dynamic, those are tend to change in API response. I was expecting the format like I have mentioned below, but at this point of given time the source provider is not able to change it as the API is used in many other different programs. And Changing the things in the source API will make other program owners to change.
b)
"value": {
"Description": "Total Calculated EQUITY AND LIABILITIES",
"YearData": [ {
"Data": "5,820,456",
"Year": "2018"
},
{
"Data": "2,957,447 ",
"Year": "2019"
} ]
}
Is there any way to overcome such thing> Any way to convert a to b?
EDIT
#Xerillio , Thanks . How can I achieve the same using below JSON format.
var json = #"
{
""entityData"": [
{
""name"": ""Statement of Comprehensive Income"",
""subattrOutput"": [
{
""name"": ""Sales"",
""subattrOutput"": [],
""value"": {
""Description"": ""Sales "",
""2018"": ""8,704,888 "",
""2019"": ""4,760,717 ""
},
""score"": ""99.5"",
""valuetype"": ""object""
},
{
""name"": ""Cost of goods sold"",
""subattrOutput"": [],
""value"": {
""Description"": ""Cost of sales "",
""2018"": ""(6,791,489) "",
""2019"": ""(3,502,785) ""
},
""score"": ""99.75"",
""valuetype"": ""object""
}
],
""value"": null,
""score"": ""98.63"",
""valuetype"": ""object""
}
]
}";
I wish this was more easy, but i just got it here:
class Program
{
static async Task Main(string[] args)
{
string json1 = #"{""value"": {""Description"": ""Total Calculated things"",""2018"": ""5,820,456 "",""2019"": ""2,957,447 ""}}";
string json2 = #"{""value"": {""Description"": ""Total Calculated EQUITY AND LIABILITIES"",""YearData"": [ {""Data"": ""5,820,456"",""Year"": ""2018""},{""Data"": ""2,957,447 "",""Year"": ""2019""} ]}}";
var obj1 = JsonConvert.DeserializeObject<ObjectResult>(json1);
var obj2 = JsonConvert.DeserializeObject<ObjectResult>(json2);
var res1 = CastObject<Result1>(obj1.Value.ToString());
var res2 = CastObject<Result2>(obj2.Value.ToString());
var invalidRes1 = CastObject<Result1>(obj2.Value.ToString());
var invalidRes2 = CastObject<Result2>(obj1.Value.ToString());
Console.WriteLine(res1);
Console.WriteLine(res2);
}
public static T CastObject<T>(string obj)
{
return !TryCastObject(obj, out T result) ? default : result;
}
public static bool TryCastObject<TOut>(string objToCast, out TOut obj)
{
try
{
obj = JsonConvert.DeserializeObject<TOut>(objToCast);
return true;
}
catch
{
obj = default;
return false;
}
}
}
public class ObjectResult
{
public object Value { get; set; }
}
public class Result1
{
[JsonProperty(PropertyName = "description")] public string Description { get; set; }
[JsonProperty(PropertyName = "2018")] public string _2018 { get; set; }
[JsonProperty(PropertyName = "2019")] public string _2019 { get; set; }
}
public class Result2
{
[JsonProperty(PropertyName = "Description")] public string Description { get; set; }
[JsonProperty(PropertyName = "YearData")] public List<YearData> YearData { get; set; }
}
public class YearData
{
[JsonProperty(PropertyName = "Data")] public string Data { get; set; }
[JsonProperty(PropertyName = "Year")] public string Year { get; set; }
}
It is A LOT of work and sincerely, as there more nodes in the json i think that using JObject is the best option, and you will have to do a deserealization in more than 1 step :(
Depending on how large the JSON structure is you could deserialize it into a Dictionary and manually map it to the proper format:
var json = #"
{
""value"": {
""Description"": ""Total Calculated things"",
""2018"": ""5,820,456 "",
""2019"": ""2,957,447 ""
}
}";
var badObj = JsonSerializer.Deserialize<BadFormat>(json);
var result = new WrapperType
{
Value = new ProperlyFormattedType()
};
foreach (var pair in badObj.Value)
{
if (pair.Key == "Description")
{
result.Value.Description = pair.Value;
}
else if (int.TryParse(pair.Key, out int _))
{
result.Value.YearData
.Add(new DatedValues
{
Year = pair.Key,
Data = pair.Value
});
}
}
// Data models...
public class BadFormat
{
[JsonPropertyName("value")]
public Dictionary<string, string> Value { get; set; }
}
public class WrapperType
{
public ProperlyFormattedType Value { get; set; }
}
public class ProperlyFormattedType
{
public string Description { get; set; }
public List<DatedValues> YearData { get; set; } = new List<DatedValues>();
}
public class DatedValues
{
public string Year { get; set; }
public string Data { get; set; }
}
See an example fiddle here.
I have the following json response
{
"Id": "1234",
"Name": "Test",
"Orders": [
{
"OrderId": "87654",
"OrderDetails": {
"OrdId": "1234",
"Name": "Desk"
}
},
{
"OrderId": "54213",
"OrderDetails": {
"OrdId": "4321",
"Name": "Table"
}
}
]
}
I want to search the list of orders to see if there is an OrderId of 87654.
I can do with an array , but how can I do it with Linq ?
You can deserialize the json string to a JObject using Newtonsoft.Json and then loop through the orders to get the OrderIDs.
var obj = JObject.Parse(json);
foreach(var order in obj["Orders"])
{
Console.WriteLine(order["OrderId"]);
}
or you can use the Where clause.
var myOrder = obj["Orders"].Where(x => x["OrderId"].ToString().Equals("87654")).FirstOrDefault();
Then you can print any property of that order,
Console.WriteLine(myOrder["OrderDetails"]["Name"].ToString());
// Prints: Desk
Alternatively, you can: also use classes to deserialize the json you have and query the orders.
public class OrderDetails
{
public string OrdId { get; set; }
public string Name { get; set; }
}
public class Order
{
public string OrderId { get; set; }
public OrderDetails OrderDetails { get; set; }
}
public class RootObject
{
public string Id { get; set; }
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
public static void Main(string[] args)
{
string json = File.ReadAllText(#"C:\temp\json.txt");
var obj = JsonConvert.DeserializeObject<RootObject>(json);
var myOrder = obj.Orders.FirstOrDefault(x => x.OrderId.Equals("87654"));
if (myOrder != null)
{
Console.WriteLine(myOrder.OrderDetails.Name);
}
}
Demo on dotnet fiddle
You can use Newtonsoft to achieve it
var jsonData = "{ 'Id': '1234', 'Name': 'Test', 'Orders': [ {'OrderId': '87654', 'OrderDetails': { 'OrdId': '1234', 'Name': 'Desk' } }, { 'OrderId': '54213', 'OrderDetails': { 'OrdId': '4321','Name': 'Table' }}]}";
var desirializedData = JsonConvert.DeserializeObject<MyObject>(jsonData);
var result = desirializedData.Orders.Where(p => p.OrderId == 87654);
foreach(var master in result)
{
Console.WriteLine(master.OrderId + " " + master.OrderDetails.Name);
}
In a WebAPI project, i have a controller that checks a status of a product, based on a value the user enters.
Lets say they enter "123" and the response should be "status": 1, AND a list of products. If they enter "321" the "status" is 0, AND a list of products.
My question is, how do i build such a string correct in a WebAPI controller.
[Route("{value:int}")]
public string GetProducts(int value)
{
var json = "";
var products = db.Products;
if (products.Any())
{
foreach (var s in products)
{
ProductApi product = new ProductApi();
product.Name = s.Name;
json += JsonConvert.SerializeObject(supplier);
}
}
var status = db.Status;
if (status.Any())
{
json += "{status:1}";
}
else
{
json += "{status:0}";
}
return json;
}
public class ProductApi
{
public string Name { get; set; }
}
Also, is this output/response considered valid?
[
{
"id":1,
"name":"product name"
},
{
"id":2,
"name":"product name 2"
},
{
"id":3,
"name":"product name 3"
}
]
{
"status": 0
}
So here are the changes for your post:
First, you should make your api return Json by default when you pass a text/html request (is this you are looking for?), adding this line to your WebApiConfig class:
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
Second, I changed the code to return a real object, impersonating your response:
public class ProductApiCollection
{
public ProductApi[] Products { get; set; }
public byte Status { get; set; }
}
public class ProductApi
{
public string Name { get; set; }
}
Method body:
public ProductApiCollection Get()
{
var result = new ProductApiCollection();
var dbProducts = db.Products;
var apiModels = dbProducts.Select(x => new ProductApi { Name = x.Name } ).ToArray();
result.Products = apiModels;
var status = db.Status.Any() ? 1 : 0;
result.Status = status;
return result;
}
This will results in the following example json:
{
"Products": [
{
"Name": "Pork"
},
{
"Name": "Beef"
},
{
"Name": "Chicken"
},
{
"Name": "Salad"
}
],
"Status": 1
}
I strongly advise you not to do manual formatting for such things, and rely on built-in and 3rd party libraries. Otherwise, you will be reinventing the things already available, tested and ready to work.
Just as raderick mentioned, you don't need to create your own custom JSON infrastructure.
public class ProductApi
{
public int Id {get;set;}
public string Name { get; set; }
}
public class ResponseDTO
{
public int Status {get;set;}
public List<ProductApi> { get; set; }
}
And in your API action, return like this:
[Route("{value:int}")]
public ResponseDTO GetProducts(int value)
{
ResponseDTO result = ...// construct response here
return result;
}
I am trying to create a web API which calls the other service and returns a array of response. The called service returns the response. I am able to get the individual item from the called service. But not sure how to build array of items and return as response from the API I am creating.
The JSON returned from the service looks like
{
"cr_response": {
"details": [{
"name": "Req",
"fields": [{
"value": "Prj0\r\nPrj1",
"name": "Project"
},
{
"value": "October 13, 2017 14:18",
"name": "Submitted"
},
{
"value": "John",
"name": "Rec Name"
}
]
}],
"cr_metadata": {}
}
}
And the POCO class looks like
public class Field
{
public string value { get; set; }
public string name { get; set; }
}
public class Detail
{
public string name { get; set; }
public List<Field> fields { get; set; }
}
public class CrMetadata
{
}
public class CrResponse
{
public List<Detail> details { get; set; }
public CrMetadata cr_metadata { get; set; }
}
public class RootObject
{
public CrResponse cr_response { get; set; }
}
Below is the code for calling the service and retrieving the response from the services
var response = await iLab_client.GetAsync(uri);
var datafile = await response.Content.ReadAsStringAsync();
var returnDataObj = JsonConvert.DeserializeObject<DTO.RootObject>(datafile);
foreach (var form in returnDataObj.cr_response.details)
{
name_response = form.name;
return Ok(name_response);
}
Here I can access the name from the details but not sure how can I access the all the name and value from the fields and construct it in a array. And send it as a JSON response.
I tried like
foreach (var form in returnDataObj.cr_response.details)
{
var id_response = form.fields;
return Ok(id_response);
}
But it throws error like
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content
type 'application/xml; charset=utf-8'.
</ExceptionMessage>
System.InvalidOperationException
To return array from Web API you need to fill your array and return it outside foreach loop:
var list = new List<string>();
foreach (...){
var name = ...
list.Add(name);
}
return Ok(list.ToArray()); // or just return Ok(list);
This is how to deserialize JSON to POCO and get the list of the names:
[TestMethod]
public void TestJsonToPocoAndGetNames()
{
const string Json = #"
{
""cr_response"": {
""details"": [{
""name"": ""Req"",
""fields"": [{
""value"": ""Prj0\r\nPrj1"",
""name"": ""Project""
},
{
""value"": ""October 13, 2017 14:18"",
""name"": ""Submitted""
},
{
""value"": ""John"",
""name"": ""Rec Name""
}
]
}],
""cr_metadata"": {}
}
}
";
var settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
};
var response = JsonConvert.DeserializeObject<RootJsonObject>(Json, settings);
var names = new List<string>();
foreach (var detail in response.CrResponse.Details)
{
names.Add(detail.Name);
foreach (var field in detail.Fields)
{
names.Add(field.Name);
}
}
Assert.AreEqual(
"Req, Project, Submitted, Rec Name",
string.Join(", ", names.ToArray()));
}
POCO classes:
public class RootJsonObject
{
[JsonProperty("cr_response")]
public CrResponse CrResponse { get; set; }
}
public class CrResponse
{
[JsonProperty("cr_metadata")]
public CrMetadata CrMetadata { get; set; }
[JsonProperty("details")]
public Detail[] Details { get; set; }
}
public class CrMetadata
{
}
public class Detail
{
[JsonProperty("fields")]
public Field[] Fields { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
public class Field
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
}