REST API Deserialize Nested Response in Newtonsoft.Json - c#

EDIT: Once I have this information deserialized, how can I access the data I need? The only information I need to grab is the TransitTime and the Cost for each accessorial.
I'm working with an API in which I need data from nested objects, but I'm unsure of how to access this information. I've passed my data to the API and have set up new classes based on how the data comes back from the API.
Here's what the response looks like:
{"RatingResponse":
{"Success":"true",
"Message":"",
"QuoteID":"57450",
"LoadNum":"57450",
"Rates":{
"Rate":[
{"SCAC":"TEST",
"CarrierName":"TEST",
"TransitTime":"1",
"ServiceLevel":"D",
"TotalCost":"983.69",
"ThirdPartyCharge":"983.69",
"Accessorials":{
"Accessorial":[
{"Code":"400",
"Cost":"1,655.55",
"Description":"Freight"
},
{"Code":"DSC",
"Cost":"-985.55",
"Description":"Discount"
},
{"Code":"FUE",
"Cost":"313.69",
"Description":"Fuel Surcharge"
}
]
},
"QuoteNumber":""
},
{"SCAC":"TEST2",
"CarrierName":"TEST2",
"TransitTime":"1",
"ServiceLevel":"D",
"TotalCost":"983.69",
"ThirdPartyCharge":"983.69",
"Accessorials":{
"Accessorial":[
{"Code":"400",
"Cost":"1,655.55",
"Description":"Freight"
},
{"Code":"DSC",
"Cost":"-985.55",
"Description":"Discount"
},
{"Code":"FUE",
"Cost":"313.69",
"Description":"Fuel Surcharge"
}
]
},
"QuoteNumber":""
}
]
},
"AverageTotalCost":"983.69"
}
}
I've converted it to C#:
public class Accessorial
{
public string Code;
public string Cost;
public string Description;
}
public class Accessorials
{
public List<Accessorial> Accessorial;
}
public class Rate
{
public string SCAC;
public string CarrierName;
public string TransitTime;
public string ServiceLevel;
public string TotalCost;
public string ThirdPartyCharge;
public Accessorials Accessorials;
public string QuoteNumber;
}
public class Rates
{
public List<Rate> Rate;
}
public class RatingResponse
{
public string Success;
public string Message;
public string QuoteID;
public string LoadNum;
public Rates Rates;
public string AverageTotalCost;
}
public class Root
{
public RatingResponse RatingResponse;
}
The only values I need are the Rate Transit Time and Service Level as well as the Accessorial Costs. I'm a beginner with APIs and am not sure how to return only that information. Any help is greatly appreciated!

you don't need any classes if you use this code
var rate = (JArray)JObject.Parse(json)["RatingResponse"]["Rates"]["Rate"];
var result = rate.Select(r => new
{
TransitTime = (int)r["TransitTime"],
ServiceLevel = (string) r["ServiceLevel"],
AccessorialCost = ((JArray)r["Accessorials"]["Accessorial"]).Select(r => (double)r["Cost"]).ToList()
});
result (in json format)
[
{
"TransitTime": 1,
"ServiceLevel": "D",
"AccessorialCost": [
1655.55,
-985.55,
313.69
]
},
{
"TransitTime": 1,
"ServiceLevel": "D",
"AccessorialCost": [
1655.55,
-985.55,
313.69
]
}
]
or you can create a Data class instead of an anonymous
List<Data> result = rate.Select(r => new Data
{
....
}).ToList();
public class Data
{
public int TransitTime { get; set; }
public string ServiceLevel { get; set; }
public List<double> AccessorialCost { get; set; }
}

I like #Serge's answer but I prefer to have results in a class because "I only need..." never holds for very long, right? Here is a good discussion of loading the JSON into a complex object:
Your JSON erred in JSON2C#.com but essentially your root object needs the other classes. Something like
public class Root
{
public RatingResponse RatingResponse;
public Rates Rates;
public Accessorials Accessorials;
}
and then deserialize into your complex object
JsonConvert.DeserializeObject<RootObject>(json)
I'm doing this off the top of my head so forgive any syntax errors.

Related

Q: How to form LINQ query to access nested attributes within class to display on Blazor WASM application

My apologies for the vast amount of code, but it is necessary for the context of the problem. I am faced with an interesting dilemma that I have not been able to solve. I am trying to access information from model called Repository. Repository contains nested classes and lists, and looks like this:
{
public User User { get; set; }
}
public class User
{
public PinnedItems PinnedItems { get; set; }
}
public class PinnedItems
{
public List<Nodes> Nodes { get; set; }
}
public class Nodes
{
public string Name { get; set; }
public string Description { get; set; }
public string Url { get; set; }
public RepositoryTopics RepositoryTopics { get; set; }
}
public class RepositoryTopics
{
public List<TopicNodes> Nodes { get; set; }
}
public class TopicNodes
{
public Topic Topic { get; set; }
}
public class Topic
{
public string Name { get; set; }
}
I have the following method within a web api controller. It is responsible for grabbing my github repositories using graphql. This method looks like this:
{
var request = new GraphQLHttpRequest
{
Query = #"query($username: String!){
user(login: $username) {
pinnedItems(first: 6, types: REPOSITORY) {
nodes {
... on Repository {
name
description
url
repositoryTopics(first:6){
nodes{
topic{
name
}
}
}
}
}
}
}
}
",
Variables = new
{
username = _configuration.GetSection("GithubUserName").Value
}
};
var graphQlResponse = await CreateClient().SendQueryAsync<Repository>(request);
var repo = new Repository
{
User = graphQlResponse.Data.User
};
return Ok(repo);
}
repo is of type Repository.
This is an example piece of JSON that comes back from testing the controller in swagger.
"pinnedItems": {
"nodes": [
{
"name": "personal-website",
"description": "My personal website",
"url": "https://github.com/personal-website",
"repositoryTopics": {
"nodes": [
{
"topic": {
"name": "blazor-webassembly"
}
},
{
"topic": {
"name": "web-api"
}
},
{
"topic": {
"name": "contentful-api"
}
},
{
"topic": {
"name": "contentful"
}
}
]
}
}
I am accessing the code in my blazor component with the following:
Repository SoftwareRepos = new Repository();
protected async override Task OnInitializedAsync()
{
SoftwareRepos = await graphQLquery.GetRepositories();
}
}
And some example code such as this gets me the list of projects as a name.
#foreach(var name in SoftwareRepos.User.PinnedItems.Nodes.Select(x => x.Name).ToArray())
{
#name
}
PRINTS OUT: name, name, name, name
Ideally I would want something that looks like this:
Project One, Description, URL, html, css, react, javascript (a list of tags)
I am having trouble trying to construct LINQ queries to access this nested information (particularly repositoryTopic -> TopicNodes -> Nodes -> Topics -> Name.
I am seeking advice on how to approach this situation, or maybe some alternative solutions to what I am doing as I suspect I am a little out of my depth here. I am using graphql.client to send and retrieve information from github.
first thing to do is to deserialize that JSON into a class structure that it represents.
public class GitResponse{
public Node[] PinnedItems {get;set;}
}
public class Node{
public string Name {get;set};
public string Description {get;set;}
....
}
etc. Once this is done the rest is easy , you just walk that tree
deserialize with
System.Text.Json.Serailizer.Deserialize<GitResponse>(json);

Trying to create model for json

How to create model for this json? I can't understand how to add dictinary to this string array.
{
"ts": 1652718271,
"updates": [
[
4,
508976,
33,
466697301,
1551996353,
"Цацу",
{
"title": " ... ",
"type": "photo"
}
]
]
}
There are a few ways to handle JSON arrays of varied types. One way is to define a class with nullable fields of the types you may encounter in the array. For example,
public class Model
{
public int TS;
public Update[][] Updates;
}
public class Update
{
public int? Number;
public string Word;
public ModelDictionary Dictionary;
}
public class ModelDictionary
{
public string Title;
public string Type;
}
Then you could access each Update with something like
if (Number != null) { ... }
else if (Word != null) { ... }
else if (Dictionary != null) { ... }
Also, https://app.quicktype.io/ is always a great resource for generating C# models from JSON objects.
With this model you can deserialize using Newtonsoft.Json
class Serial
{
public string ts { get; set; }
public object [][] updates { get; set; }
}

Send a post request in JSON format

Below is the format of post request expected in JSON. Can anyone please tell me how to achieve this.
{
"MywebServiceInputDetail":{
"MyDatalst":{
"MyData":[
{
"name":"TestName",
"id":"2611201",
"SomeRefVal":"REF123456"
}
]
}
}
}
I am using JavaScriptSerializer as of now.
Below is the code.
[Serializable]
public struct MyStruct
{
public string name;
public string id;
public string refno;
}
JavaScriptSerializer jss = new JavaScriptSerializer();
string serializedJson = jss.Serialize(ObjMystrcut);
Above code results in the JSON string as
{"name":"TestName","id":"1234567","refno":"567123"}
I am new to JSON so I'm not able to formulate the request format.
I am avoiding to achieve it by hardcoding a json string. Basically, I am trying to understand what does { and [ bracketing mean. Does [ mean that I need to create an array of objects?
You can do something like this:
string serializedJson = jss.Serialize(new { MywebServiceInputDetail = new { MyDatalst = new { MyData = new[] { ObjMystrcut } } } });
{} is object notation, so it represents an object, with properties.
[] is an array notation.
Yes, the [ and ] symbols represent JSON arrays (collections of objects). In your example, MyData is a collection of those structs you created.
You need to create the following classes:
public class MywebServiceInputDetail
{
public MyDatalst MyDatalst { get; set; }
}
public class MyDatalst
{
public List<MyStruct> MyData { get; set; }
}
public struct MyStruct
{
public string name;
public string id;
public string SomeRefVal;
}
Now create a MywebServiceInputDetail object and serialize it.
Personally I would forget about using a struct and just create the following class instead:
public class MyClass
{
public string Name { get; set; }
public string Id { get; set; }
public string SomeRefVal { get; set; }
}
You should also add JSON attributes to the properties to make sure Name and Id are serialized with lowercase letters.

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/

json newtonsoft : Deserialize Object containing a list of string

I have the following issue with this json :
{
"EVTS": {
"EVT": [
{ "ID": "123456",
"KEY1" : "somekey",
"CATEG": [
"cat1",
"cat2",
"cat3"
]
}
]}
}
and this c# class:
public class myClass{
public string ID { get; set; }
public string KEY1 { get; set; }
public list<string> CATEG { get; set; }
}
public class ESObject1
{
[JsonProperty("EVT")]
public List<myClass> EVT { get; set; }
}
public class ESObject0
{
[JsonProperty("EVTS")]
public ESObject1 EVTS { get; set; }
}
}
here i call the deserializer :
ESObject0 globalobject = JsonConvert.DeserializeObject<ESObject0>(json);
But this last code doesnt work, i throws this exception : System.ArgumentException: Could not cast or convert from System.String to System.Collections.Generic.List1[System.String].`
Instead of list<string> i used string [] and only string nothing seems to work.
how can i deserialize this object correctly please.
Thank you.
There doesn't seem to be any apparent problem wit hyour code as this working example illustrates:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
public class myClass
{
public string ID { get; set; }
public string KEY1 { get; set; }
public List<string> CATEG { get; set; }
}
public class ESObject1
{
[JsonProperty("EVT")]
public List<myClass> EVT { get; set; }
}
public class ESObject0
{
[JsonProperty("EVTS")]
public ESObject1 EVTS { get; set; }
}
class Program
{
static void Main()
{
string json =
#"{
""EVTS"": {
""EVT"": [
{
""ID"": ""123456"",
""KEY1"": ""somekey"",
""CATEG"": [
""cat1"",
""cat2"",
""cat3""
]
}
]
}
}";
ESObject0 globalobject = JsonConvert.DeserializeObject<ESObject0>(json);
foreach (string item in globalobject.EVTS.EVT[0].CATEG)
{
Console.WriteLine(item);
}
}
}
Maybe you just fed a wrong json value to the deserializer which doesn't look like as the one shown in your question. By the way, the one shown i nyour question is invalid JSON as you are missing a , after KEY1 property declaration.
UPDATE:
Now that you have shown your real JSON (coming from http://donnees.ville.quebec.qc.ca/Handler.ashx?id=69&f=JSON) it appears that there's a row where CATEG is not an array of strings but a simple string:
""CATEG"": ""Conférence""
Now that's a pretty bad design because they are mixing arrays and simple properties. I am afraid that in order to deal with this situation you will need to use JObjects and extract the information you need by testing the actual underlying type.
For example:
var obj = JObject.Parse(json);
var events = (JArray)obj["EVTS"]["EVT"];
foreach (JObject evt in events)
{
var categories = evt["CATEG"];
if (categories is JArray)
{
// you've got a list of strings so you can loop through them
string[] cats = ((JArray)categories)
.Select(x => x.Value<string>())
.ToArray();
}
else
{
// you've got a simple string
string cat = categories.Value<string>();
}
}
I have done this many times with many many headaches. My advice is take the json output and use a tool similar to this to write your class for you (http://json2csharp.com/).
Then go over any nullable variables and add nullable type (ex. using int? for int) where needed.

Categories