C# API requests which return a class - c#

Hey all (newbie here) I am developing A Xamarin forms application In which I am already making API requests (Code is Down Below), However, the response I am getting back at the moment looks like this:
{
"result": "success",
"documentation": "https://www.exchangerate-api.com/docs",
"terms_of_use": "https://www.exchangerate-api.com/terms",
"time_last_update_unix": 1673308801,
"time_last_update_utc": "Tue, 10 Jan 2023 00:00:01 +0000",
"time_next_update_unix": 1673395201,
"time_next_update_utc": "Wed, 11 Jan 2023 00:00:01 +0000",
"base_code": "EUR",
"target_code": "GBP",
"conversion_rate": 0.8803
}
I am Only using the conversion rate variable however in the next API I am hoping to use all this variables are stored in parameters (Class I guess?) called Data, so currently, the class I am using to store the variable which is grabbed from this API response looks like this:
public double conversion_rate { get; set; }
So how would I adapt this code to interpret that data, the response is below (I want the data labeled "actual" in the "intensity" section TIA):
"data": [
{
"from": "2023-01-10T19:30Z",
"to": "2023-01-10T20:00Z",
"intensity": {
"forecast": 70,
"actual": 79,
"index": "low"
}
}
]
Ive attempted to find a solution on my own for a while, looked all around and still nothing to see :)

I would create two DTO (data transfer object) classes which contain the properties you're interested in. Once you make the API request, you can map or parse the relevant fields to another object or type.
FYI - you should rename conversion_rate to ConversionRate, which is the standard for property names in C# and then add [JsonPropertyName("conversion_rate")] above the property.

First, you can write the c# class entity based on the json file. Here is the demo and you can refer to.
public class Root
{
public string result { get; set; }
public string documentation { get; set; }
public string terms_of_use { get; set; }
public int time_last_update_unix { get; set; }
public string time_last_update_utc { get; set; }
public int time_next_update_unix { get; set; }
public string time_next_update_utc { get; set; }
public string base_code { get; set; }
public string target_code { get; set; }
public double conversion_rate { get; set; }
}
Then, you can write the method to deserialize the json file.
using System.Text.Json;
var options = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var codeRoot = JsonSerializer.Deserialize<Root>(Json, options);

Related

Nested objects in Swashbuckle auto-generated example

I have a .NET Core API that is documented via Swashbuckle and Swagger. The "example" that is generated for the UI doesn't seem to be including nested objects in the request properly, although they are handled and processed correctly when executing the endpoints.
I have a CreatePaymentRequest class, which is received from the body of the HTTP request, which contains a property of a Notional type. Notional is made up of a decimal value, and a string value.
The generated example looks like this:
{
"tradeId": 0,
"settlementMeans": "SWIFT",
"notional1": {},
"notional1Rate": 0,
"notional2": {},
"notional2Rate": 0,
"paymentReference": "string",
"description": "string"
}
When I would expect it to look like this:
{
"tradeId": 0,
"settlementMeans": "SWIFT",
"notional1": {"Amount": 0, "Currency": "string"},
"notional1Rate": 0,
"notional2": {"Amount": 0, "Currency": "string"},
"notional2Rate": 0,
"paymentReference": "string",
"description": "string"
}
The generated schema also seems to be off, as it doesn't include the properties for Notional:
Though it is listed properly in the "Schemas" section at the bottom:
Here are the classes that make up each object:
public class Request
{
public long TradeId { get; set; }
public SettlementMeans SettlementMeans { get; set; }
public Notional Notional1 { get; set; }
public decimal Notional1Rate { get; set; }
public Notional Notional2 { get; set; }
public decimal Notional2Rate { get; set; }
public string PaymentReference { get; set; } = "";
public string Description { get; set; } = "";
}
public class Notional
{
[JsonContructor]
public Notional(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
public Notional()
{
Amount = 0;
Currency = "XXX";
}
public decimal Amount { get; }
public string Currency { get; }
}
Controller method:
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CreatePaymentApi_Response))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(BadRequestResponse))]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<CreatePaymentApi_Response>> PostCreate(CreatePaymentApi_Request request)
{
var command = _mapper.Map<CreatePaymentHandler.Request>(request);
var response = await _mediator.Send(command);
var result = _mapper.Map<CreatePaymentHandler.Response>(response);
return Ok(result);
}
I am setting up SwaggerGen using services.AddSwaggerGen(); in ConfigureServices without any special options.
Having to constantly type out, or copy / paste the JSON for the Notional values is starting to become very repetitive when debugging via the Swashbuckle UI. Is there anything I can do to get the example to generate as I expect it to?
I was eventually able to track this down to the readonly Amount and Currency properties on Notional. I was expecting it to use the constructor marked with JsonConstructor, but apparently, this is only supported when using the newtonsoft serializer, NOT System.Text.Json.
In theory, I should be able to work around this using a custom ISchemaFilter, but I've decided that it isn't worth it just yet to implement one.

Json.Net Deserialize dictionary without property name

I already looked at a lot of other questions with the same problem but never found a definitive solution that actually works for me. I tried using the JsonExtensionData Attribute, that doesn't work though since I can't convert my other data class to an object and it throws the Invalid extension data attribute on 'NAMESPACE'. Member 'Sols' type must implement IDictionary<string, JToken>. error.
My current data model class looks like this
public partial class Mars
{
public Dictionary<string, Sol> Sols { get; set; }
[JsonProperty("sol_keys")]
public List<long> SolKeys { get; set; }
}
public partial class Sol
{
[JsonProperty("AT")]
public At At { get; set; }
[JsonProperty("First_UTC")]
public DateTimeOffset FirstUtc { get; set; }
[JsonProperty("Last_UTC")]
public DateTimeOffset LastUtc { get; set; }
[JsonProperty("Season")]
public string Season { get; set; }
}
public partial class At
{
[JsonProperty("av")]
public double Av { get; set; }
[JsonProperty("ct")]
public long Ct { get; set; }
[JsonProperty("mn")]
public double Mn { get; set; }
[JsonProperty("mx")]
public double Mx { get; set; }
}
The json data looks like this
{
"651":
{
"AT":
{
"av": -61.957,
"ct": 302204,
"mn": -96.733,
"mx": -15.877
},
"First_UTC": "2020-09-25T02:42:14Z",
"Last_UTC": "2020-09-26T03:21:49Z",
"Season": "summer"
},
"652": {
"AT": {
"av": -65.002,
"ct": 278608,
"mn": -96.111,
"mx": -15.653
},
"First_UTC": "2020-09-26T03:21:50Z",
"Last_UTC": "2020-09-27T04:01:24Z",
"Season": "summer"
},
"sol_keys": [
"646",
"647",
"648",
"649",
"650",
"651",
"652"
]
}
I can't really modify the json data since I get it from an api.
I basically just want to select one of the numbers and then get the Sol data of that object.
Any help would be appreciated.
The JSON doesn't fit well with the C# type system. However, you can still use Json.Net to parse it. You just need to introduce some extra steps.
First step is to parse the JSON to a JObject:
var jObject = JsonConvert.DeserializeObject<JObject>(json);
Then you can extract the sol_keys:
var solKeys = jObject.GetValue("sol_keys").ToObject<long[]>();
Now it becomes a bit tricky. If you remove the sol_keys from the JSON (in this case the parsed JSON) it has the structure of a dictionary of Sol objects that you are able to parse:
jObject.Remove("sol_keys");
var mars = jObject.ToObject<Dictionary<long, Sol>>();
Now you have both solKeys and mars parsed from the JSON. Furthermore the solKeys and the keys in the dictionary share the same type (long).

Deserialize JSON array of values in C#

I've got some difficulties with this json script:
{
"insured_agent_flag": "a",
"id": "1",
"agent": {
"fullName": "John Travolta",
"mobileNumberPdf": "+987654321",
"mobileNumber": "",
"identityCard": {
"identityCardExpirationDate": null
},
"secondIdentityCard": {
"identityCardExpirationDate": null
},
"notes": {},
"sign": "ADVANCED"
},
"basicData": {
"personType": "PERSON",
"agreeWithCompleteAnalysis": false,
"investmentInterest": false
},
"nonOfferedProducts": [
"PROD_A",
"PROD_B",
"PROD_C"
]
}
I would like to get some parameters from this script and put it into sql server table.
In order to do that, I used and transformed a C# script shared by https://mycontraption.com:
using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.Web.Script.Serialization;
using Microsoft.SqlServer.Dts.Pipeline;
namespace SC_c7e2d8c3918d46a5a07a1b438ddc7642
{
public class BasicData
{
public string agreeWithCompleteAnalysis { get; set; }
public string inOtherSystem { get; set; }
public string investmentInterest { get; set; }
}
public class ParentObject
{
public BasicData BasicData { get; set; }
public int id { get; set; }
public string insured_agent_flag { get; set; }
public IEnumerable<string> NonOfferedProducts { get; set; }
}
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
JavaScriptSerializer js = new JavaScriptSerializer();
// Give the input column a variable to make it easier to reference.
BlobColumn combinedColumn = Row.parameterscon;
// Convert from blob to string
string reviewConverted = System.Text.Encoding.ASCII.GetString(combinedColumn.GetBlobData(0, Convert.ToInt32(combinedColumn.Length)));
// Deserialize the string
ParentObject obj = js.Deserialize<ParentObject>(reviewConverted);
var rows = obj.NonOfferedProducts.ToList();
Row.agreeWithCompleteAnalysis = obj.BasicData.agreeWithCompleteAnalysis;
Row.inOtherSystem = obj.BasicData.inOtherSystem;
Row.investmentInterest = obj.BasicData.investmentInterest;
Row.projectionid = obj.id;
Row.insuredagentflag = obj.insured_agent_flag;
//Row.nonOfferedProducts =
}
}
}
For 'standard' objects it works fine, but there is a problem with array "nonOfferedProducts". After compiling I get an error:
„object reference not set to an instance of an object”.
Here are my questions:
1. How should I handle 'nonOfferedProducts' array in C# script?
2. Why do I get foregoing error?
3. Unfortunately there exists a possibility, that json scripts would have some errors, like missing braces. How should I handle that?
Thank you!
Thanks a lot for your answers. According to your comments I'll try to give you more explanations:
1. The json script I have added in this post - it's only small part of whole script. In complete script there is a lot of different parameters. What is more, my C# code should scan about 40.000 json scripts (stored in sql server table in one column). These scripts has got similiar structure - but not the same.
So I thought about C# resolution, that will be searching for the parameters that I need. For json scripts without these parameters the c# code will put nulls to the right output columns.
Here are my output columns:
-agreeWithCompleteAnalysis
-inOtherSystem
-investmentInterest
-projectionId
-insuredAgentFflag
-nonOfferedProducts
I understood, that structure of my classes were wrong - I'll improve that.
But I've got one doubt - is it possible to prepare c# code structure, that will handle only these parameters I need?
And finally, I would like to put the results into my database.
For example if nonOfferedProducts property will have 3 values (not always!), I'd like to send to my database table 3 records (3 different values for nonOfferedProducts column and 3 the same values for the rest columns -agreeWithCompleteAnalysis, inOtherSystem etc).
I hope that will be clear now.
Thanks a lot for your help!
J
Use https://quicktype.io and paste json, it will generate c# model and serializer code.
As I said in my comment, your c# model doesn't match the JSON object.
If the model was made up of various nested objects to better reflect the actual JSON then you'll have more luck:
public class IdentityCard
{
public DateTime? IdentityCardExpirationDate { get; set; }
}
public class Notes
{
//No idea what should be in here...
}
public class BasicData
{
public string PersonType { get; set; }
public bool AgreeWithCompleteAnalysis { get; set; }
public bool InvestmentInterest { get; set; }
}
public class Agent
{
public string FullName { get; set; }
public string MobileNumberPdf { get; set; }
public string MobileNumber { get; set; }
public IdentityCard IdentityCard { get; set; }
public IdentityCard SecondIdentityCard { get; set; }
public Notes Notes { get; set; }
public string Sign { get; set; }
}
//Note: THIS is the actual class that matches the JSON sample given.
public class ParentObject
{
public string insured_agent_flag { get; set; }
public int Id { get; set; }
public Agent Agent { get; set; }
public BasicData BasicData { get; set; }
public IEnumerable<string> NonOfferedProducts { get; set; }
}
Once the model is correct, then Deserialization works fine for me with the given example (I did this in a unit test, but assuming your string matches your example this should be fine)
//get json
string json = #"
{
""insured_agent_flag"": ""a"",
""id"": ""1"",
""agent"": {
""fullName"": ""John Travolta"",
""mobileNumberPdf"": ""+987654321"",
""mobileNumber"": """",
""identityCard"": {
""identityCardExpirationDate"": null
},
""secondIdentityCard"": {
""identityCardExpirationDate"": null
},
""notes"": {},
""sign"": ""ADVANCED""
},
""basicData"": {
""personType"": ""PERSON"",
""agreeWithCompleteAnalysis"": false,
""investmentInterest"": false
},
""nonOfferedProducts"": [
""PROD_A"",
""PROD_B"",
""PROD_C""
]
}";
var js = new JavaScriptSerializer();
ParentObject obj = js.Deserialize<ParentObject>(json);
//do things...
var rows = obj.NonOfferedProducts.ToList();
Assert.AreEqual(3, rows.Count);
Assert.AreEqual("PROD_A", rows.First());
The asserts pass - This code happily gets the list of strings in the NonOfferedProducts property with the given example.
Obviously if you cannot rely on the consistency of the JSON (either structure or how well-formed it is) then you'll have problems, but that's a different issue.
To answer your question no 2) you are getting the object reference error because the BasicDataClass.nonOfferedProducts is null and you are trying iterate over it , this may be a reason that you are sending the wrong json which JavaScriptSerializer is not able to deserilize.
your 3rd question you can always validate your json with json validators which are there online like https://jsonformatter.org/

API.AI .NET C# SDK - Parameters appearing as {object}

I'm using API.AI and integrating via the .NET SDK for API.AI found in Nuget and using a webhook MVC controller to catch the POST. For passing string and int data is received fine and saved fine, but if I sent an entity type in API.AI of say #sys.age, the parameter received into the Webook exists as I expect but the value is of type {object}. I've tried the following to get the data value out of this {object}:-
(dynamic)item.Value
item.Value.ToString()
(dynamic)item.Value.ToString()
where item.Value shows {object} in the watch window.
Can anyone suggest how to get the value back from this?
Thanks in advance.
Json of parameters of #sys.age is:
"result": {
"parameters": {
"age": {
"amount": 10,
"unit": "year"
}
},
Then you can use classes to capture values in C# like:
public class Age
{
public int amount { get; set; }
public string unit { get; set; }
}
public class RequestParameter
{
public Age age { get; set; }
}
public class RequestBody
{
public RequestParameter parameters { get; set; }
}
[HttpPost]
public ActionResult GetValue(RequestBody result)
{
var age = result.parameters.age.amount;
...
You can see system entities at: https://api.ai/docs/reference/system-entities

Questioning received JSON structure

I'm currently using a beta API (http://developer.riotgames.com/api/methods) which returns JSON for all the exposed methods.
I've been able to use JSON.NET to deserialize all of these return values so far. However, today I consumed one of their function which returns a JSON that is valid but is in my opinion not correct.
You're probably wondering, why don't you ask it on the beta forum? I have but I haven't received an answer so far and in general this intrigues me.
A snippet of the JSON return:
"1001": {
"name": "Boots of Speed",
"plaintext": "Slightly increases Movement Speed",
"group": "BootsNormal",
"description": "<...
}
The problem I have with this structure is that the ID is used as a "group" without an identifier. I would be able to use this decently if it had
"ItemID" : "1001"
But it doesn't have that. I don't mind manually parsing it but I'd first like to know whether or not this JSON is correct (not just valid).
Do you agree that this is not a clean way of creating a JSON block that contains a list of elements or am I missing something here? So far I haven't seen any comments on the beta forum of this API so I'm really wondering why.
Edit "valid" vs "correct/usable":
I know it's a valid JSON statement. I'm questioning the fact whether this is usable with JSON.NET.
I have the following class definition (with two subclasses):
public class JSONItem
{
[JsonProperty("tags")]
public string[] Tags { get; set; }
[JsonProperty("plaintext")]
public string Plaintext { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("into")]
public string[] Into { get; set; }
[JsonProperty("image")]
public JSONItemImage Image { get; set; }
[JsonProperty("colloq")]
public string Colloq { get; set; }
[JsonProperty("gold")]
public JSONItemGold Gold { get; set; }
}
When giving the above JSON block to to JSONConvert.DeserializeObject(json) it throws an error because "1001" is not mentioned in JSONItem.
How do you handle this so that you can use JSON.NET?
A class like this won't work because you have no names to give the properties:
public class JSONItemWrapper
{
[JsonProperty("")]
public string ID { get; set; }
[JsonProperty("")]
public JSONItem MyProperty { get; set; }
}
Edit: "consistent with other methods"
The other methods return blocks where every property is within {} and has an identifier. The most recently added function have this "primary key outside of {}" style.
It is a valid json and you can use a type like Dictionary<string, SomeObject> to deserialize your json.
string json = #"{
""1001"": {
""name"": ""Boots of Speed"",
""plaintext"": ""Slightly increases Movement Speed"",
""group"": ""BootsNormal"",
""description"": ""desc...""
}
}";
var dict = JsonConvert.DeserializeObject<Dictionary<string, MyObject>>(json);
and accesing an item later on by its key can be fast too.
public class MyObject
{
public string name { get; set; }
public string plaintext { get; set; }
public string group { get; set; }
public string description { get; set; }
}
It's annoying when APIs do things like this (using numbers as property names), but all is not lost. Simply deserialize the JSON using Json.NET and then access each of the items using the indexer operator on the parent object.
EDIT:
I almost never create DTOs when deserializing JSON. It's lots of unnecessary boilerplate in most cases. I prefer deserializing to a dynamic object, but that won't be as effective when dealing with property names that begin with digits.
Here is how I would deserialize your sample message:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonExample
{
internal class Program
{
private static void Main()
{
const string json = #"
{
'1001': {
'name': 'Boots of Speed',
'plaintext': 'Slightly increases Movement Speed',
'group': 'BootsNormal',
'description': '<...'
}
}";
var jObject = JsonConvert.DeserializeObject<JObject>(json);
var plaintext = jObject["1001"]["plaintext"].Value<string>();
Console.WriteLine(plaintext);
}
}
}
When put into http://JSONLint.com,
{
"1001": {
"name": "Boots of Speed",
"plaintext": "Slightly increases Movement Speed",
"group": "BootsNormal",
"description": "<..."
}
}
Validates as JSON.

Categories