I'm new in json and I have a little problem here that I can't solve.
In other json I have {"certificate":"123456789",} and I get the value of that using.
model.cs
class Login //this code is working.
{
[JsonProperty("certificate")]
public string certificate { get; set; }
}
But my problem is when I come up with json dictionary I don't have any Idea how can i get the value from it.
this is my code from controller.cs to get the json from Saba/api/component/location
using(var client = new HttpClient()) //I'm sure this code is working.
{
client.BaseAddress = new Uri(string.Format("https://{0}/", HostUrl));
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("SabaCertificate", certificate);
//HTTP GET: /Saba/api/componet/location
HttpResponseMessage response = await client.GetAsync("Saba/api/component/location");
if(response.IsSuccessStatusCode)
{
Location saba = await response.Content.ReadAsAsync<Location>();
//I always get empty location when I try to run the app.
}
}
This is JSON with dictionary.
{
"results": [
{
"loc_name": "Aberdeen - Aberdeen Town Square_489",
"id": "locat000000000001877",
"href": "https://na1.sabacloud.com/Saba/api/component/location/locat000000000001877"
}]
}
How can I get the value of json dictionary?
What I've tried is
Model.cs
[JsonProperty("results")] \\ AggregateException Error
class Location
{
[JsonProperty("loc_name")]
public string loc_name { get; set; }
[JsonProperty("id")]
public string id { get; set; }
[JsonProperty("href")]
public string href { get; set; }
}
But I can't retrieve the value.
Based on the JSON posted in your question you should have something like
public class Result
{
public string loc_name { get; set; }
public string id { get; set; }
public string href { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
then just call
var rootObject = JsonConvert.DeserializeObject<RootObject>(jsonData);
You can always use JSON Formatter & Validator to test if your JSON is valid, and json2csharp to generate c# classes from json.
Related
I am new to c# json.
I would only want to get CardTransactions array , "status": 0, and ignore the rest of the json values.
May I know how to achieve with c#.
{
"response":{
"timeStamp":7812371,
"totalCount":1,
"CardTransactions":[
{
"transactionDate":"2021-08-16",
"invoiceNo":"KM011782313",
"amount":2000.00
}
],
"status":0,
"message":"dakjalsda"
},
"status":null,
"message":null
}
Create a model class that looks that way
public class Response
{
[JsonProperty("CardTransactions")]
public List<CardTransaction> CardTransactions { get; set; }
[JsonProperty("status")]
public int Status { get; set; }
}
public class Root
{
[JsonProperty("response")]
public Response Response { get; set; }
}
and use Newtonsoft to convert JSON to object like the following
Root response = JsonConvert.DeserializeObject<Root>(yourJson);
I am calling the ServiceNow Incidents table and pulling back one incident like this. https://mydevInstance.service-now.com/api/now/v1/table/incident?sysparm_limit=1
var client = new RestClient("https://mydevInstance.service-now.com/api/now/v1/table/incident?sysparm_limit=1");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Basic myAuthKey");
IRestResponse response = client.Execute(request);
The JSON it returns in RESTSharp looks like this.
{
"result": [
{
"parent": "",
"made_sla": "true",
"caused_by": "",
"watch_list": "",
"upon_reject": "cancel",
"resolved_by": {
"link": "https://mydevInstance.service-now.com/api/now/v1/table/sys_user/5137153cc611227c000bbd1bd8cd2007",
"value": "5137153cc611227c000bbd1bd8cd2007"
},
"approval_history": "",
"number": "INC0000060"
}
]
}
How do I create a C# list or array of all the Keys under result? I can't Serialize the object with JSON.Net because additional keys can be added over time.
You need to grab the sample of the JSON content, then make a C# class using the 'Paste Special' option I described.
Then you can use the JsonConvert.DeserializeObject<T> (in a nuget package by Newtonsoft) to deserialize your web service response in a C# object instance.
Here are the C# classes I generated with your JSON object unaltered:
public class Rootobject
{
public Result[] result { get; set; }
}
public class Result
{
public string parent { get; set; }
public string made_sla { get; set; }
public string caused_by { get; set; }
public string watch_list { get; set; }
public string upon_reject { get; set; }
public Resolved_By resolved_by { get; set; }
public string approval_history { get; set; }
public string number { get; set; }
}
public class Resolved_By
{
public string link { get; set; }
public string value { get; set; }
}
You use this type like this:
var json = "t-b-d"; // From Web Service call
Rootobject response = JsonConvert.DeserializeObject<Rootobject>(json);
// use the response object.
** UPDATED **
If you need a more flexible model, all JSON will deserialize into Dictionary<string, string>, but I have found that serialization / deserialization results are more reliable when the model is consistent
var response = JsonConvert.DeserializeObject<Dictionary<string,string>>(json);
Here is what does work using System.Text.Json
var incidentFields = new List<string>();
var doc = JsonDocument.Parse(json);
foreach (var o in doc.RootElement.GetProperty("result").EnumerateArray())
{
foreach (var p in o.EnumerateObject())
{
incidentFields.Add(p.Name.ToString());
}
}
I created a library that handles that by default. (You can add custom types also)
https://autodati.github.io/ServiceNow.Core/
Normally, serialized objects would be used from the services to the webapi calls but in this instance I have to use a json representation for the call.
The process would be to deserialize the json to the proper class, then process as usual.
HttpClient Put
Method is called from within a console app
public async Task<ApiMessage<string>> PutAsync(Uri baseEndpoint, string relativePath, Dictionary<string, string> headerInfo, string json)
{
HttpClient httpClient = new HttpClient();
if (headerInfo != null)
{
foreach (KeyValuePair<string, string> _header in headerInfo)
_httpClient.DefaultRequestHeaders.Add(_header.Key, _header.Value);
}
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json-patch+json"));
var content = new StringContent(json, Encoding.UTF8, "application/json-patch+json");
var response = await httpClient.PutAsync(CreateRequestUri(relativePath, baseEndpoint), content);
var data = await response.Content.ReadAsStringAsync();
...
}
Endpoint
The call never hits the endpoint. The endpoint is hit if I remove the [FromBody] tag but as expected, the parameter is null. There seems to be some sort of filtering happening.
[HttpPut()]
[Route("")]
[SwaggerResponse(StatusCodes.Status200OK)]
[SwaggerResponse(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdatePaymentSync([FromBody] string paymentSyncJson)
{
if (string.IsNullOrEmpty(paymentSyncJson))
return BadRequest();
//hack: don't have access to models so need to send json rep
var paymentSync = JsonConvert.DeserializeObject<PaymentSync>(paymentSyncJson);
....
}
This is the json payload. I thought [FromBody] took care of simple types but this is proving me wrong.
{
"paymentSyncJson": {
"id": 10002,
"fileName": "Empty_20190101.csv",
"comments": "Empty File",
"processingDate": "2019-01-02T19:43:11.373",
"status": "E",
"createdDate": "2019-01-02T19:43:11.373",
"createdBy": "DAME",
"modifiedDate": null,
"modifiedBy": null,
"paymentSyncDetails": []
}
}
Just expanding on my Comment.
The OP did:
[HttpPut()]
[Route("")]
[SwaggerResponse(StatusCodes.Status200OK)]
[SwaggerResponse(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdatePaymentSync([FromBody] string paymentSyncJson)
{
if (string.IsNullOrEmpty(paymentSyncJson))
return BadRequest();
//hack: don't have access to models so need to send json rep
var paymentSync = JsonConvert.DeserializeObject<PaymentSync>(paymentSyncJson);
....
}
Where they have put [FromBody] string paymentSyncJson, FromBody will try and deserialise into the type you specify, in this case string. I suggest doing:
public async Task<IActionResult> UpdatePaymentSync([FromBody] JObject paymentSyncJson)
Then you can change this line:
var paymentSync = JsonConvert.DeserializeObject<PaymentSync>(paymentSyncJson);
To:
var paymentSync = paymentSyncJson.ToObject<PaymentSync>();
Your payload is not a string, it's a json, that's why the runtime can't parse the body to your requested string paymentSyncJson.
To solve it, create a matching dto which reflects the json
public class PaymentDto
{
public PaymentSyncDto PaymentSyncJson { get; set; }
}
public class PaymentSyncDto
{
public int Id { get; set; }
public string FileName { get; set; }
public string Comments { get; set; }
public DateTime ProcessingDate { get; set; }
public string Status { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
public DateTime ModifiedDate { get; set; }
public string ModifiedBy { get; set; }
public int[] PaymentSyncDetails { get; set; }
}
Then use it in the controller method to read the data from the request body
public async Task<IActionResult> UpdatePaymentSync([FromBody] PaymentDto payment)
i use RestSharp to access a Rest API. I like to get Data back as an POCO.
My RestSharp Client looks like this:
var client = new RestClient(#"http:\\localhost:8080");
var request = new RestRequest("todos/{id}", Method.GET);
request.AddUrlSegment("id", "4");
//request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
//With enabling the next line I get an new empty object of TODO
//as Data
//client.AddHandler("*", new JsonDeserializer());
IRestResponse<ToDo> response2 = client.Execute<ToDo>(request);
ToDo td=new JsonDeserializer().Deserialize<ToDo>(response2);
var name = response2.Data.name;
my Class for the JsonObject looks like this:
public class ToDo
{
public int id;
public string created_at;
public string updated_at;
public string name;
}
and the Json Response:
{
"id":4,
"created_at":"2015-06-18 09:43:15",
"updated_at":"2015-06-18 09:43:15",
"name":"Another Random Test"
}
Per the documentation, RestSharp only deserializes to properties and you're using fields.
RestSharp uses your class as the starting point, looping through each
publicly-accessible, writable property and searching for a
corresponding element in the data returned.
You need to change your ToDo class to the following:
public class ToDo
{
public int id { get; set; }
public string created_at { get; set; }
public string updated_at { get; set; }
public string name { get; set; }
}
I generate a C# class for a json source using json2csharp.com. My json is:
{
"email_verified": true,
"user_id": "gg2323",
"app_metadata": {
"tc_app_user": {
"user_guid": "c0fb150f6er344df98ea3a06114e1e4a",
"cto_admin_a_user_id": "551294d4f6cfb46e65a5aq71",
"lang": "EN",
"country": "USA",
"disabled": false
}
and my resulting C# is:
public class TcAppUser
{
public string user_guid { get; set; }
public string cto_admin_a_user_id { get; set; }
public string lang { get; set; }
public string country { get; set; }
public bool disabled { get; set; }
}
public class AppMetadata
{
public TcAppUser tc_app_user { get; set; }
public int logins_count { get; set; }
}
public class RootObject
{
public bool email_verified { get; set; }
public string user_id { get; set; }
public AppMetadata app_metadata { get; set; }
}
Using the .NET HttpClient GET, I can read into this C# structure from the JSON API quite nicely. Going the other way (POST, PATCH) poses a problem: my app_metadata property name is dropped in the generated JSON output when I use a common approach like:
//Would be nice: var contentIn = new ObjectContent<string>(RootObjectInstance.app_metadata, new JsonMediaTypeFormatter());
string json = JsonConvert.SerializeObject(RootObjectInstance.app_metadata);
HttpResponseMessage response = await hclient.PatchAsync("api/users/" + user_id, new StringContent(json, Encoding.UTF8, "application/json"));
The resulting JSON is now:
{
"tc_app_user": {
"lang": "en-en",
"country": "GER",
"disabled": false
}
}
My quick hack is to use the following additional wrapper to dynamically repackage the app_metadata property so it has the same format going out that it had coming in. The rest remains the same as above:
dynamic wireFormatFix = new ExpandoObject();
wireFormatFix.app_metadata = usr.app_metadata;
string json = JsonConvert.SerializeObject(wireFormatFix);
Now my JSON output corresponds to the JSON input. My question: what is best-practice to achieve symmetric json input and output here without a pesky format fix?
EDIT: If I try to PATCH the entire structure (RootObjectInstance instead of RootObjecInstance.app_metadata) I get:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Payload validation error: 'Additional properties not allowed: 'user_id'."
}
So, I must either send the app_metadata subset/property of the C# RootObject, properly packaged, or I must selectively delete fields from the RootObject to meet the API's requirements.
Thanks!
The root app_metadata tag is being removed from your JSON because you're simply not serializing it. This:
string json = JsonConvert.SerializeObject(RootObjectInstance.app_metadata);
Will serialize everything that is inside app_metadata.
If you serialized the entire object graph, you wouldn't need to patch anything:
string json = JsonConvert.SerializeObject(RootObjectInstance);
As a side note, you should follow C# naming conventions. You can use JsonProperty to help you with that.
Edit:
Ok, after your edit i see the actual problem. You're calling an API by user_id in your query string, and you also have a user_id property inside your object. This seems like you need two different objects for the job.
You have a couple of possibilities:
Create an object hierarchy:
public class BaseObject
{
[JsonProperty(email_verified)]
public bool EmailVerified { get; set; }
[JsonProperty(app_metadata)]
public AppMetadata AppMetadata { get; set; }
}
public class ExtendedObject : BaseObject
{
[JsonProperty(user_id)]
public string UserId { get; set; }
}
And then use the base type to serialize the data:
var baseObj = new BaseObject(); // Fill the object properties.
var json = JsonConvert.SerializeObject(intermidiateObj);
HttpResponseMessage response = await hclient.PatchAsync("api/users/" +
user_id,
new StringContent(json,
Encoding.UTF8,
"application/json"));
Use an anonymous object which includes only properties you actually need:
var intermidiateObj = new { app_metadata = usr.app_metadata };
var json = JsonConvert.SerializeObject(intermidiateObj);
HttpResponseMessage response = await hclient.PatchAsync("api/users/" +
user_id,
new StringContent(json,
Encoding.UTF8,
"application/json"));