Deserialize JSON nested arrays to DataTable using C# - c#

I need help deserialize JSON output to a datatable in C# using Newtsonsoft.json (JSON.NET). I have looked at many examples an diskussions on this and many other forums but i can not figure out how to do it.
JSON sample to deserialize:
[{
"display_name": "Check MWExternal",
"plugin_output": "MWExternal.exe: not running",
"host": {
"name": "WIN2008.arlaplast.local"
} },{
"display_name": "Swap usage",
"plugin_output": "Paging File usage is = 39.19 %",
"host": {
"name": "srvdccz01.arlaplast.local"
}},{
"display_name": "Swap usage",
"plugin_output": "Paging File usage is = 40 %",
"host": {
"name": "srvdccz02.arlaplast.local"
}}]
The response structure always looks the same but can have many blocks
I want this in a table looking like this
Here are my code so far: (Only gives me First two columns in table, not the host.name)
public partial class test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
PopulateList();
}
public class Host
{
public string name { get; set; }
}
public class RootObject
{
public string display_name { get; set; }
public string plugin_output { get; set; }
public Host host { get; set; }
}
public static DataTable ToDataTable<T>(List<T> items)
{
DataTable dataTable = new DataTable(typeof(T).Name);
//Get all the properties
System.Reflection.PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Defining type of data column gives proper data table
var type = (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(prop.PropertyType) : prop.PropertyType);
//Setting column names as Property names
dataTable.Columns.Add(prop.Name, type);
}
foreach (T item in items)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//inserting property values to datatable rows
values[i] = Props[i].GetValue(item, null);
}
dataTable.Rows.Add(values);
}
//put a breakpoint here and check datatable
return dataTable;
}
public void PopulateList()
{
string uri = "https://op5.ateavdc.se/api/filter/query?query=[services]%20state!=0&columns=display_name,plugin_output,host.name";
string _auth = string.Format("{0}:{1}", "USERNAME", "PASSWÒRD");
string _enc = Convert.ToBase64String(Encoding.ASCII.GetBytes(_auth));
string _cred = string.Format("{0} {1}", "Basic", _enc);
try
{
dynamic webRequest__1 = (HttpWebRequest)WebRequest.Create(uri);
webRequest__1.Headers.Add("Authorization", _cred);
webRequest__1.ContentType = "application/json; charset=utf-8";
webRequest__1.Method = "GET";
dynamic webResponse = (HttpWebResponse)webRequest__1.GetResponse();
if (webResponse.StatusCode == HttpStatusCode.OK)
{
dynamic reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
TextBox1.Text = s.ToString();
var data = JsonConvert.DeserializeObject<List<RootObject>>(s);
DataTable dt = ToDataTable(data);
GridView1.DataSource = dt;
GridView1.DataBind();
}
else
{
Response.Write(webResponse.StatusCode);
}
}
catch (Exception ex)
{
Response.Write(ex.Message);
}}}

Thinking that you know how the nested Json is going to come up, based on the example provided, lets create the class,
public class Host
{
public string name { get; set; }
}
public class RootObject
{
public string display_name { get; set; }
public string plugin_output { get; set; }
[JsonProperty("host")]
public Host h { get; set; }
public string Host { get { return this.h.name;} set {this.h.name = value;}}
}
Now lets do the DeserializeObject using NewtonSoft
string s = "[{\"display_name\": \"Check MWExternal\",\"plugin_output\": \"MWExternal.exe: not running\",\"host\": { \"name\": \"WIN2008.arlaplast.local\"} },{\"display_name\": \"Swap usage\",\"plugin_output\": \"Paging File usage is = 39.19 %\",\"host\": { \"name\": \"srvdccz01.arlaplast.local\"}},{\"display_name\": \"Swap usage\",\"plugin_output\": \"Paging File usage is = 40 %\",\"host\": { \"name\": \"srvdccz02.arlaplast.local\"}}]";
JsonConvert.DeserializeObject<List<RootObject>>(s);
You will get the output as below,
Then if you filter against that using linq,
JsonConvert.DeserializeObject<List<RootObject>>(s).Select(x => new {x.display_name, x.plugin_output, x.Host})
Hope this helps..!

Simplest solution:
var json = File.ReadAllText("data.json");
var arr = JArray.Parse(json);
var data = arr.Select(x => new
{
DisplayName = x.Value<string>("display_name"),
PluginOutput = x.Value<string>("plugin_output"),
Host = x.Value<JToken>("host").Value<string>("name")
})
.ToList();

Related

custom response object for model validation in asp.net core

I want custom object in response of API having [required] data annotation on model properties like this:
{
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "fatal",
"code": "required",
"location": [
"/f:AllergyIntolerance/f:status"
]
}
]
}
Is it possible to do it or I would have to code it.
Because model validation happens before action is called, is there any way I can do it?
To create custom request and respond samples for your api, you can use Swashbuckle.AspNetCore.Swagger and to improve validations on your models you can use FluentValidations Sample. Good Luck!
first for simplify define your models like these :
public class ResponseModel
{
public string resourceType { get; set; }
public List<ResponseIssueModel> issue { get; set; } = new List<ResponseIssueModel>();
}
public class ResponseIssueModel
{
public string severity { get; set; }
public string code { get; set; }
public List<string> locations { get; set; } = new List<string>();
}
Then on your actions you can return this :
var response = new ResponseModel();
response.resourceType = "OperationOutcome";
response.issue.Add(new ResponseIssueModel
{
severity = "fatal",
code = "required",
locations = { "/f:AllergyIntolerance/f:status" }
});
return Ok(response);
you can use Builder Pattern for easy create response object
If you want to validate your model in controller,you could try with TryValidateModel method as mentioned in the document:
I tried as below:
in controller:
var model = new TestModel() { Id=1,nestedModels=new List<NestedModel>() { new NestedModel() { Prop1="P11"} } };
var isvalid=TryValidateModel(model);
var errorfiledlist = new List<string>();
if (!isvalid)
{
foreach (var value in ModelState.Values)
{
foreach (var error in value.Errors)
{
errorfiledlist.Add(MidStrEx(error.ErrorMessage,"The "," field"));
}
}
}
var jsonstring = JsonSerializer.Serialize(model);
foreach (var field in errorfiledlist)
{
var oldstr = String.Format("\"{0}\":null", field);
var newstr = String.Format("\"{0}\":\"required\"", field);
jsonstring = jsonstring.Replace(oldstr, newstr);
};
var obj = JsonSerializer.Deserialize<Object>(jsonstring);
return Ok(obj);
MidStrEx method:
public static string MidStrEx(string sourse, string startstr, string endstr)
{
string result = string.Empty;
int startindex, endindex;
try
{
startindex = sourse.IndexOf(startstr);
if (startindex == -1)
return result;
string tmpstr = sourse.Substring(startindex + startstr.Length);
endindex = tmpstr.IndexOf(endstr);
if (endindex == -1)
return result;
result = tmpstr.Remove(endindex);
}
catch (Exception ex)
{
}
return result;
}
Models:
public class TestModel
{
public int Id { get; set; }
[Required]
public string Prop { get; set; }
public List<NestedModel> nestedModels { get; set; }=new List<NestedModel>();
}
public class NestedModel
{
public string Prop1 { get; set; }
[Required]
public string Prop2 { get; set; }
}
The result:

c# Save JSON response to Database table

Ok I tried this for a couple of days now.
I'm trying to read JSON response and insert/update it to DB table (already exists) based on a condition from the response. I have created a Console app.
What my JSON looks like:
[
{
"logtime", "2022-10-19T08:50:06.515Z");
"lat_value", "Some-Latitude-Value");
"long_value", "Some-Longitude-Value");
"name", "Name"
"population", "Population"
"unemployment_rate", "UnemploymentPercentage"
},
{
"logtime", "2022-10-19T08:50:06.515Z");
"lat_value", "Some-Latitude-Value");
"long_value", "Some-Longitude-Value");
"name", "Name"
"population", "Population"
"unemployment_rate", "UnemploymentPercentage"
},
{
"logtime", "2022-10-19T08:50:06.515Z");
"lat_value", "Some-Latitude-Value");
"long_value", "Some-Longitude-Value");
"name", "Name"
"population", "Population"
"unemployment_rate", "UnemploymentPercentage"
},
...
]
Condition is:
if(UnemploymentPercentage > 70)
{
if(Name matches to NameCol && UnemploymentPercentage != UnemploymentRateCol)
{ //InsertRecord }
else
{ //UpdateUnemploymentPercentageValue }
}
What I have tried:
ModelClass.cs:
public string LogTime { get; set; }
public string Lat { get; set; }
public string Long { get; set; }
public string Name { get; set; }
public string Population { get; set; }
public decimal UnemploymentRate { get; set; }
ControllerClass.cs:
string url = "https://my-url-goes-here.net";
ModelClass dataModel = new();
public void JsonParser()
{
var clients = new RestClient(url);
var request = new RestRequest("api/constructorName", Method.Get)
{
Timeout = -1
};
request.AddHeader("api-key", "myKeyGoesHere");
request.AddParameter("param1", "someParam");
RestResponse response = clients.Execute(request);
if(response.Content != null)
{
string result = response.Content;
_ = JsonConvert.DeserializeObject<List<ModelClass>>(result);
}
}
public void WriteJsonDataToMSSql()
{
//var rates = ((JObject)json["feild6"]).Properties().Select(x => new { Feild6 = x.Name, Rate = Convert.ToDecimal(x.Value.ToString()) });
using (SqlConnection sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["Conn"].ConnectionString))
{
sqlCon.Open();
using (SqlBulkCopy copy = new SqlBulkCopy(sqlCon))
{
copy.DestinationTableName = "tbl_name";
copy.ColumnMappings.Add("LogTime", "logtime");
copy.ColumnMappings.Add("Lat", "Latitude");
copy.ColumnMappings.Add("Long", "Longitude");
copy.ColumnMappings.Add("Name", "Name");
copy.ColumnMappings.Add("Population", "Population");
copy.ColumnMappings.Add("UnemploymentRate", "Unemployment");
copy.WriteToServer(output.AsDataReader());
}
sqlCon.Close();
}
}
I don't know how much I am right and what to do next. Please feel free to correct me. Any help is appreciated.
Thanks in advance.

C# and jSON is not working

Need a little help here
So I have a json url and need to get each item into a for each loop
Here is the json
{
"_links": { },
"count": 9,
"list": {
"staff": [
"staff1",
"staff2",
"staff3"
],
"clients": [
"client1",
"client2",
"client3",
"client4",
"client5",
"client6"
]
}
}
I have also got the following code in c# but keep getting errors
string source;
var sURL = "LINK_TO_JSON_URL";
WebRequest req = HttpWebRequest.Create(sURL);
req.Method = "GET";
req.Timeout = 5000;
try
{
using (StreamReader reader = new StreamReader(req.GetResponse().GetResponseStream()))
{
source = reader.ReadToEnd();
reader.Close();
}
JToken jObject = JObject.Parse(source);
string clients = (string)jObject["list"]["clients"];
//for each loop here
}
catch (Exception ex)
{
//error message here
}
What is it that I am doing wrong? I have tried to convert string to array and still get nothing.
I want to be able to get each clients names
Cheers
So here is some code to show iterating over a JArray that contains strings. This code has been tested with your json and outputs each client string.
var jObject = JObject.Parse(source);
foreach (var client in jObject["list"]["clients"])
{
Console.WriteLine((string)client);
}
Look at this piece of code
#region Model For Deserialize String to Object
public class Links
{
}
public class List
{
public List<string> staff { get; set; }
public List<string> clients { get; set; }
}
public class RootObject
{
public Links _links { get; set; }
public int count { get; set; }
public List list { get; set; }
}
#endregion
#region Restfull Respone (String) Convert To RootObject
public class ConvertStringToObj
{
public void Execute()
{
//Your webReq and Response progress here
string jsonResponse="";
var rootObj = (RootObject)Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse, typeof(RootObject));
foreach(var eachItem in rootObj.list.staff)
{
var stafName = eachItem;
}
}
}
#endregion
Here is an solution, you can do this via Objects :)

C# How to iterate through list for individual API calls

From the following JSON data I want to put the station_code variables into a list. After this I want to use each station_code variable into a API URL call.
{
"request_time": "2015-02-19T08:33:39+00:00",
"stations": [
{
"station_code": "HWV",
"atcocode": null
},
{
"station_code": "HXX",
"atcocode": null
},
{
"station_code": "HAF",
"atcocode": null
}
]
}
This is the C# code I have attempting this
dynamic array = JsonConvert.DeserializeObject(json);
dynamic stations = array.stations;
var test = JObject.Parse(json);
var services = test.SelectTokens("stations[*].station_code").Select(t => (string)t).ToList();
JArray items = new JArray();
foreach (JObject station in stations)
{
items.Add(station["station_code"]);
}
int stationLength = 3;
for (int i = 0; i < stationLength; i++)
{
string localJson = get_local_departs("http://transportapi.com/v3/uk/train/station/"+ items[i] + "/live.json?app_id=03bf8009&app_key=d9307fd91b0247c607e098d5effedc97&train_status=passenger");
dynamic localStationArray = JsonConvert.DeserializeObject(localJson);
dynamic departures = localStationArray.departures;
dynamic localStationDeparts = departures.all;
foreach (var depart in localStationDeparts)
{
//info put into data tables
}
}
}
Well why can't you convert that to a strong type and use that instead like
public class stations
{
public string station_code { get; set; }
public string atcocode {get; set; }
}
public class Data
{
public DateTime request_time {get; set; }
public IEnumerable<stations> Stations {get; set; }
}
var result = JsonConvert.DeserializeObject<Data>(json);
foreach(stations s in result.Stations)
{
Console.WriteLine(s.station_code);
}

Schema dependent class to schemaless document using MongoDb and C#

Let us suppose we have a document to store our client which has fixed and extra fields.
So here goes our sample class for the client:
public class Client
{
public string Name{ get; set; }
public string Address{ get; set; }
public List<ExtraField> ExtraFields{ get; set; } //these fields are extra ones
}
In extra field class we have something like this:
public class ExtraField
{
public string Key{ get; set; }
public string Type { get; set; }
public string Value { get; set; }
}
If I use standard driver's behaviour for serialization I would get smth like this:
{{Name:VName, Address:VAddress, ExtraFields:[{Key:VKey,Type:VType,
Value:VValue},...]}, document2,...,documentn}
While I would like to have something like this:
{{Name:VName, Address:VAddress, VKey:VValue,...}, document2,...,documentn}
This would improve the search performance and is generally the point of document orientation.
How can I customize the serialization to such a way?
Here is the way I solved it (it works fine) and solved the issue.
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
namespace TestDataGeneration {
public class FieldsWrapper : IBsonSerializable
{
public List<DataFieldValue> DataFieldValues { get; set; }
public object Deserialize(MongoDB.Bson.IO.BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
{
if (nominalType != typeof(FieldsWrapper)) throw new ArgumentException("Cannot deserialize anything but self");
var doc = BsonDocument.ReadFrom(bsonReader);
var list = new List<DataFieldValue>();
foreach (var name in doc.Names)
{
var val = doc[name];
if (val.IsString)
list.Add(new DataFieldValue {LocalIdentifier = name, Values = new List<string> {val.AsString}});
else if (val.IsBsonArray)
{
DataFieldValue df = new DataFieldValue {LocalIdentifier = name};
foreach (var elem in val.AsBsonArray)
{
df.Values.Add(elem.AsString);
}
list.Add(df);
}
}
return new FieldsWrapper {DataFieldValues = list};
}
public void Serialize(MongoDB.Bson.IO.BsonWriter bsonWriter, Type nominalType, IBsonSerializationOptions options)
{
if (nominalType != typeof (FieldsWrapper))
throw new ArgumentException("Cannot serialize anything but self");
bsonWriter.WriteStartDocument();
foreach (var dataFieldValue in DataFieldValues)
{
bsonWriter.WriteName(dataFieldValue.LocalIdentifier);
if (dataFieldValue.Values.Count != 1)
{
var list = new string[dataFieldValue.Values.Count];
for (int i = 0; i < dataFieldValue.Values.Count; i++)
list[i] = dataFieldValue.Values[i];
BsonSerializer.Serialize(bsonWriter, list);
}
else
{
BsonSerializer.Serialize(bsonWriter, dataFieldValue.Values[0]);
}
}
bsonWriter.WriteEndDocument();
}
} }
Essentially you just need to implement two methods yourself. First one to serialize an object as you want and second to deserialize an object from db to your Client class back:
1 Seialize client class:
public static BsonValue ToBson(Client client)
{
if (client == null)
return null;
var doc = new BsonDocument();
doc["Name"] = client.Name;
doc["Address"] = client.Address;
foreach (var f in client.ExtraFields)
{
var fieldValue = new BsonDocument();
fieldValue["Type"] = f.Type;
fieldValue["Value"] = f.Value;
doc[f.Key] = fieldValue;
}
return doc;
}
2 Deserialize client object:
public static Client FromBson(BsonValue bson)
{
if (bson == null || !bson.IsBsonDocument)
return null;
var doc = bson.AsBsonDocument;
var client = new Client
{
ExtraFields = new List<ExtraField>(),
Address = doc["Address"].AsString,
Name = doc["Name"].AsString
};
foreach (var name in doc.Names)
{
var val = doc[name];
if (val is BsonDocument)
{
var fieldDoc = val as BsonDocument;
var field = new ExtraField
{
Key = name,
Value = fieldDoc["Value"].AsString,
Type = fieldDoc["Type"].AsString
};
client.ExtraFields.Add(field);
}
}
return client;
}
3 Complete test example:
I've added above two method to your client class.
var server = MongoServer.Create("mongodb://localhost:27020");
var database = server.GetDatabase("SO");
var clients = database.GetCollection<Type>("clients");
var client = new Client() {Id = ObjectId.GenerateNewId().ToString()};
client.Name = "Andrew";
client.Address = "Address";
client.ExtraFields = new List<ExtraField>();
client.ExtraFields.Add(new ExtraField()
{
Key = "key1",
Type = "type1",
Value = "value1"
});
client.ExtraFields.Add(new ExtraField()
{
Key = "key2",
Type = "type2",
Value = "value2"
});
//When inseting/saving use ToBson to serialize client
clients.Insert(Client.ToBson(client));
//When reading back from the database use FromBson method:
var fromDb = Client.FromBson(clients.FindOneAs<BsonDocument>());
4 Data structure in a database:
{
"_id" : ObjectId("4e3a66679c66673e9c1da660"),
"Name" : "Andrew",
"Address" : "Address",
"key1" : {
"Type" : "type1",
"Value" : "value1"
},
"key2" : {
"Type" : "type2",
"Value" : "value2"
}
}
BTW: Take a look into serialization tutorial as well.

Categories