Convert Json to C# class using IEnumerable<> - c#

I have a Json which looks like this...
{
"steps": [
{
"stepsType": "runWizard",
"wizardType": "cv2.0Server",
"config": {
"mode": "add",
"resourceName": "cv2.0 Server",
"activeDbPrimaryServer": {
"serverName": "JJH3M005A",
"serverAddress": "JJH3M005A.microsoft.info"
},
"activeDbCatalog": "cv2.0Database",
"activeDBUserId": "user",
"activeDBPassword": "password",
"activeIntegratedSecurity": false
}
}
]
}
I have created a model like this...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Database.Configuration.Model
{
public class ClusterConfigurationModel
{
public IEnumerable<ClusterConfigurationModelStep> Steps { get; }
}
public class ClusterConfigurationModelStep : ClusterConfigurationModel
{
public string StepType { get; }
public string WizardType { get; }
public IEnumerable<ClusterConfigurationServerConfig> Config { get; }
}
public class ClusterConfigurationServerConfig : ClusterConfigurationModelStep
{
public string Mode { get; }
public string ResourceName { get; }
public IEnumerable<ClusterConfigurationDbPrimaryServer> ActiveDbPrimaryServer { get; }
public string ActiveDbCatalog { get; }
public string ActiveDbUserId { get; }
public string ActiveDbPassword { get; }
public string ActiveIntegratedSecurity { get; }
}
public class ClusterConfigurationDbPrimaryServer : ClusterConfigurationServerConfig
{
public string ServerName { get; }
public string ServerAddress { get; }
}
}
Is this correct I am trying to create a custom json convertor using,
AbstractJsonConverter
JsonConverterFactory with an Interface
JsonConverter with an Interface
I am new to C#, just want to know whether the model I have created is correct or do I need some modification?

First of all, you need not to have inheritance for every single class. For example (after removing inheritance):
public class ClusterConfigurationModelStep : ClusterConfigurationModel
{
public string StepType { get; }
public string WizardType { get; }
public IEnumerable<ClusterConfigurationServerConfig> Config { get; }
}
Second, you may want to have [JsonProperty("json_key_name")] from Newtonsoft.Json or [JsonPropertyName("json_key_name")] from System.Text.Json for mapping JSON keyfield name to class's variable. It is by default using camelCase in C#.

You should avoid using IEnumerable<T> for fields and properties, use a T[] array or an IList<T> instead.
The reason for this is that an enumerable is an interator that contains state, it contains a reference to the current item being pointed to. This means that it can only be consumed once, and then you can not enumerate the contents again.
Instead, you usually want to create a list instead and then enumerate that list multiple times in multiple places in your code.

Related

Getting empty(blank) value while printing JSON field (value) using JsonConverter.Deserialize in C#

I am trying to parse a JSON using NewtonSoft (my JSON is an array type). Below is what it looks like...
{
"steps": [
{
"stepsType": "runWizard",
"wizardType": "demoServer",
"config": {
"mode": "add",
"resourceName": "demo server 1",
"activeDbPrimaryServer": {
"serverName": "abc",
"serverAddress": "abc.demo.local"
},
"activeDbCatalog": "demoActiveDB",
"activeDBUserId": "sa",
"activeDBPassword": "xyz",
}
}
]
}
I have created a class using JsonToC# convertor....
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ServerSetupWizardConsoleApp
{
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public class ActiveDbPrimaryServer
{
public string serverName { get; set; }
public string serverAddress { get; set; }
}
public class Config
{
public string mode { get; set; }
public string resourceName { get; set; }
public ActiveDbPrimaryServer activeDbPrimaryServer { get; set; }
public string activeDbCatalog { get; set; }
public string activeDBUserId { get; set; }
public string activeDBPassword { get; set; }
}
public class Step
{
public string stepsType { get; set; }
public string wizardType { get; set; }
public Config config { get; set; }
}
public class Root
{
public List<Step> steps { get; set; }
}
}
And now when I am trying to deserialise it in another class, I am getting a empty response in console...
I have a TEXT from where I am reading the JSON and then storing it in a string type variable and the using the string to deserialise it.
public Object ReadJson()
{
string jsonText = File.ReadAllText("C:\\Desktop\\demo.json");
var rootObject = (Root) JsonConvert.DeserializeObject(jsonText, typeof(Root));
var activeDbAttr = (ActiveDbPrimaryServer)JsonConvert.DeserializeObject(jsonText, typeof(ActiveDbPrimaryServer));
Console.WriteLine("Value : " + activeDbAttr.serverAddress);
}
This activeDbAttr.serverAddress is giving me nothing in CONSOLE
It print --> value : (nothing after ":" like blank)
Can someone tell me what is wrong here. I followed some old answers, not getting to a point where I can fix it.
Live demo : https://dotnetfiddle.net/PuO8F8
It's a simple navigation issue.
You deserialize to a Root object, and instead of navigating with the property you deserialize again into an other thing, hopping to land in the right place.
var rootObject = JsonConvert.DeserializeObject<Root>(jsonText);
var activesDBs = json.steps.Select( x=> x.config.activeDbPrimaryServer).ToList();
Result:
Dumping object(System.Linq.SelectListIterator`2[Step,ActiveDbPrimaryServer])
[
{
serverAddress : abc.demo.local
serverName : abc
}
]
Why did it failed ?
var jsonDB = JsonConvert.DeserializeObject<ActiveDbPrimaryServer>(GetJson());
Tell the Parser that the Json is of type ActiveDbPrimaryServer.
The parser open the Json and find the first object:
{
"steps": [..]
}
Look for the property of the expected type ActiveDbPrimaryServer
public class ActiveDbPrimaryServer
{
public string serverName { get; set; }
public string serverAddress { get; set; }
}
And find nothing. It end there and give you an object of the right type with no property initialized
Partial deserialization:
If you want to deserialize only the part you need, refer to this documentation from Newtonsoft:
Deserializing Partial JSON Fragments
JObject rootObj = JObject.Parse(GetJson());
// get JSON result objects into a list
IList<JToken> results = rootObj["steps"].Children() // Step is a list so we use `.Children()`
["config"]["activeDbPrimaryServer"].ToList();
IList<ActiveDbPrimaryServer> dbResults = new List<ActiveDbPrimaryServer>();
foreach (JToken result in results)
{
// JToken.ToObject is a helper method that uses JsonSerializer internally
ActiveDbPrimaryServer dbResult = result.ToObject<ActiveDbPrimaryServer>();
dbResults.Add(dbResult);
}
you are deserializing
var rootObject = (Root) JsonConvert.DeserializeObject(jsonText, typeof(Root));
var activeDbAttr = (ActiveDbPrimaryServer)JsonConvert.DeserializeObject(jsonText, typeof(ActiveDbPrimaryServer));
[the second one yields null ... and null cast to ActiveDbPrimaryServer yields null.]
the same string twice into 2 different types of objects that do not share common base class (of course other than object).
your text represents either a Root or a ActiveDbPrimaryServer not both. the ActiveDbPrimaryServer is a member of the config class and that in turn of Step. ....
step through your graph root.Step.Config .. if all the instances are set you should reach your instance of ActibeDdPrimaryServer

Extracting value from json string C#

My json string is as follows:
{
"data": {
"order_reference": "7000016543",
"package_data": [
{
"tracking_no": "34144582723408",
"package_reference": "7000016543"
}
]
},
"success": true
}
How do I get tracking_no from this json.
I tried using
dynamic jsonObj = JsonConvert.DeserializeObject(bytesAsString);
and then
foreach (var obj in jsonObj.items)
{
}
but it only yields order reference.
You can create Types for your JSON:
namespace Example
{
using System;
using System.Net;
using System.Collections.Generic;
using Newtonsoft.Json;
public class MyObject
{
[JsonProperty("data")]
public Data Data { get; set; }
[JsonProperty("success")]
public bool Success { get; set; }
}
public class Data
{
[JsonProperty("order_reference")]
public string OrderReference { get; set; }
[JsonProperty("package_data")]
public PackageDatum[] PackageData { get; set; }
}
public class PackageDatum
{
[JsonProperty("package_reference")]
public string PackageReference { get; set; }
[JsonProperty("tracking_no")]
public string TrackingNo { get; set; }
}
}
then you can deserialize using JsonConvert.Deserialize<MyObject>(input);
the problem about your code is: there is no collection on your root json object. I mean it is not an object. Since it is just an object and it's child you can access is directly. And then you have a collection package_data. So you can go for loop in it.
Here is the schema of your json data.
foreach(dynamic item in jsonObj.item.package_data)
{
//item.tracking_no;
//item.package_reference
}

Deserialize json data into list in c#

I am calling an external web service and this is what I get in response after posting to their server:
{
"status":200,
"data":{
"h21":{
"total_price":{
"acacia":{
"available":0,
"price":null,
"availability":false
},
"maple":{
"available":7,
"price":2399.0,
"availability":true
}
}
},
"h17":{
"total_price":{
"mahogany":{
"available":1,
"price":1899.0,
"availability":true
},
"oak":{
"available":0,
"price":null,
"availability":false
},
"maple":{
"available":6,
"price":1649.0,
"availability":true
}
}
}
}
}
I want this response to be converted into a list. I used jsontocsharp online converter to generate class and use code below:
var Jsonresult = JsonConvert.DeserializeObject<Sstageback.Models.Sstage.treeboRoomTypes.RootObject>(JsonReplace);
But the thing is mine is a dynamic JSON response which can change over course of time.
Note: The response which I get from the server is hotel and its room availability so while generating classes I can't generate with a single class file since the hotel id's may change also the room types and its availability also changes.
Example: h21 is one hotel id and total_price has its room type details similarly h17 is the next hotel and total_price has its room type details.
Basically, TotalPrice should be a Dictionary<string, Availability> or similar. It's not clear what list you'd have, but that's naturally a dictionary. That's then nested within a dictionary at the top level.
Sample code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
public class Response
{
public int Status { get; set; }
public Dictionary<string, Hotel> Data { get; set; }
}
public class Hotel
{
[JsonProperty("total_price")]
public Dictionary<string, Room> TotalPrice { get; set; }
}
public class Room
{
public int Available { get; set; }
public decimal? Price { get; set; }
public bool Availability { get; set; }
}
class Test
{
static void Main(string[] args)
{
var text = File.ReadAllText("test.json");
var response = JsonConvert.DeserializeObject<Response>(text);
foreach (var pair in response.Data)
{
Console.WriteLine($"Key: {pair.Key}");
foreach (var nestedPair in pair.Value.TotalPrice)
{
var room = nestedPair.Value;
Console.WriteLine($" {nestedPair.Key}: {room.Available}/{room.Price}/{room.Availability}");
}
}
}
}
Output:
Key: h21
acacia: 0//False
maple: 7/2399.0/True
Key: h17
mahogany: 1/1899.0/True
oak: 0//False
maple: 6/1649.0/True
You'll need to make a DTO model that corresponds with the response. The value of a attribute can change, that's no problem, as long as the type stays the same ( an int stays an int and a string stays a string).
Your Object could look like this:
public class Room{
public int Available { get; set;}
public int Price { get; set; }
public bool availability { get; set; }
}
public class Hotel{
public string Name { get; set; }
public List<Room> Rooms { get; set; }
}
You should convert Serialize and Deserialize this. This is only an example, you want your model to be 100% the same as your JSON.
For an easy conversion between Models and DTO, you could use AutoMapper: http://automapper.org/

Returning an object derived from an interface with generic list

My application reads in JSON from disk and deserialising using JSON.net; which is working fine.
My JSON is laid out like this:
{
"driver": {
"driverTag": "blah_blah",
"driverName": "Blah Blah",
"driverTransport": "serial-device"
},
"devices": [
{
"deviceName": "Dev1",
"deviceTag": "DEV1",
"deviceStartMode": "Auto"
},
{
"deviceName": "Dev2",
"deviceTag": "DEV2",
"deviceStartMode": "Auto"
}
]
}
Based on the "driverTransport" value, I deserialise to either a SerialDriverConfig, TelnetDriverConfig, SNMPDriverConfig... etc class.
As the "driver" properties will be the same for every driver, no matter the transport type, I have a "DriverConfigTemplate" class. The "devices" will differ from JSON file to JSON file and have specific properties for that transport type (i.e. a serial device will have properties like "serialPortName", "serialBaudRate" etc.)
I have a "DriverConfig" interface, where T is "DeviceConfig".
public interface DriverConfig<T> where T : DeviceConfig
{
DriverConfigTemplate driver { get; set; }
List<T> devices { get; set; }
}
My device config is as follows:
public class DeviceConfig : IDeviceConfig
{
public string deviceTag { get; set; }
public string deviceName { get; set; }
public string deviceStartMode { get; set; }
}
Now; the problem part. When I am deserialising, I check the transport type before hand and determine the class to use; i.e for a serial driver I will use the "SerialDriverConfig" class and deserialise using the "SerialDeviceConfig":
public class SerialDeviceConfig : DeviceConfig
{
public int serialComPort { get; set; }
public int serialBaudRate { get; set; }
public int serialDataBits { get; set; }
public string serialParity { get; set; }
public string serialStopBits { get; set; }
public string serialHandshake { get; set; }
public int serialReadTimeout { get; set; }
public int serialWriteTimeout { get; set; }
public bool serialRtsEnable { get; set; }
public bool serialDtrEnable { get; set; }
}
My "SerialDriverConfig" class looks like this:
public class SerialDriverConfig : DriverConfig<SerialDeviceConfig>
{
public DriverConfigTemplate driver { get; set; }
public List<SerialDeviceConfig> devices { get; set; }
}
Again, this is fine and the JSON.net deserialiser does its job perfectly.
I have a function that gets called when the JSON config file has been loaded and validated against its respective schema, then passed on to a "DeserialiseDriverConfig" function where I am trying to return the derived driver object; which is where I am stuck :(
private DriverConfig<DeviceConfig> DeserialiseDriverConfig(string _json, string _driverTransport)
{
switch (_driverTransport)
{
case "serial-device":
try
{
SerialDriverConfig _serialDriverConfig = JsonConvert.DeserializeObject<SerialDriverConfig>(_json);
if (_serialDriverConfig != null)
{
return _serialDriverConfig;
}
}
catch (Exception e)
{
//Blah blah blah
}
break;
}
return null;
}
I have been stuck on this one for a few days, have tried many things and this is where I have ended up. I am getting "Cannot implicitly convert type "SerialDriverConfig" to "DriverConfig". An explicit conversion exists (are you missing a cast?)" So I understand why this error is occurring, but cannot get around it.
Hope my code makes sense and someone can help me out here?
You can change your DriverConfig class to be non-generic
public interface DriverConfig
{
DriverConfigTemplate driver { get; set; }
List<DeviceConfig> devices { get; set; }
}
and instead of using derived classes (SerialDriverConfig etc.) you can set Json.net to deserialize to the correct DeviceConfig type based on either having a $type attribute in your JSON like this or using a custom JsonConverter similar to this
I'm not sure if this solution fits your need but if you create your method and SerialDriverConfig with using generic type T you can use your interface as a returning type. Can you try the code below;
Your Method:
private static DriverConfig<T> DeserialiseDriverConfig<T>(string _json, string _driverTransport)
{
switch (_driverTransport)
{
case "serial-device":
try
{
SerialDriverConfig<T> _serialDriverConfig = JsonConvert.DeserializeObject<SerialDriverConfig<T>>(_json);
if (_serialDriverConfig != null)
{
return _serialDriverConfig;
}
}
catch (Exception e)
{
//Blah blah blah
}
break;
}
return null;
}
SerialDriverConfig Class:
public class SerialDriverConfig<T> : DriverConfig<T>
{
public DriverConfigTemplate driver { get; set; }
public List<T> devices { get; set; }
}
Also you should consider changing DriverConfig<T> interface approach because if you leave it as-is you will have boxing issue. If you do not need you may remove where T : DeviceConfig from your interface or modify it according to your current circumstances.
Hope this helps, please let me know if this works for you

Parse complex JSON: multiple loops vs. classes

I have a Json of type :
{
"JobProcessors": [
{
"JobName": "ArchivalJob",
"IsEnabled": true,
"Batching": {
"BatchSize": 0,
"DegreeOfParallelism": -1
},
"Settings": {
"ArchivalJobCollectionPageSize": 50
}
},
{
"JobName": "AuditLogJob",
"IsEnabled": false,
"Batching": {
"BatchSize": 10,
"DegreeOfParallelism": -1
},
"Settings": {}
}
],
"ScheduledJobs": [
{
"JobName": "RemoteStartClientCommandJob",
"PrimaryAction": {
"ConnectionString": "#JobProcessorsIntegrationSBConnectionStringValue#",
"Settings": {
"LeadTimeInSeconds": "600",
"MaxSrsJobCount": 25
}
},
"ErrorAction": {
"ConnectionString": "#PairedJobProcessorIntegrationSBConnectionStringValue#",
"EntityPath": "remotestartqueue",
"Settings": {
"LeadTimeInSeconds": "600",
"MaxSrsJobCount": 25
}
}
}
]
}
I want to check the "IsEnabled" property for all "JobName" for which come under "JobProcessors" category.
In C# what i Have used till now is :
dynamic parsedJson = JsonConvert.DeserializeObject(reader.GetString(1));
foreach (var item in parsedJson)
{
foreach (var smallitem in item)
{
foreach (var tag in smallitem)
{
if(tag.IsEnabled.toString()=="true"){
Console.WriteLine("true");
}
}
}
}
This is giving me correct result except the fact that it also iterates for "ScheduledJobs" . But the main issue is :
Is this the right or most efficient way to do this ? If possible suggest some better method .
One that i know of is using classes , but i may not know the json structure beforehand. Also the json is very huge so making classes can be cumbersome !!
Given that you are already doing JObject.Parse(jsonstring); to parse your JSON string, you can use SelectTokens() with a JSONPath query to find all "JobName" objects under "JobProcessors":
// I want to check the "IsEnabled" property for all "JobName" for which come under "JobProcessors"
foreach (var job in root.SelectTokens("..JobProcessors[?(#.JobName)]"))
{
var isEnabled = (bool?)job["IsEnabled"];
Debug.WriteLine(string.Format("Job {0}: IsEnabled={1}", job["JobName"], isEnabled));
}
Notes:
.. is the recursive descent operator: it recursively descends the JToken hierarchy returning each item, subsequently to be matched against the remaining parts of the query string.
JobProcessors returns values of properties of that name.
[?(#.JobName)] returns array items (of JobProcessors in this case) that are objects with a JobName property.
(bool?) casts the value of "IsEnabled" to a boolean or null if missing.
And the output of this is:
Job ArchivalJob: IsEnabled=True
Job AuditLogJob: IsEnabled=False
As in your code snippet we are using two foreach it may take time for large object. So we can do the same thing in a single foreach or if you have some specific node to fetch or search we can use linq, and for this first we need to convert our json object into c# object. For converting Json object to C# you can use this site "http://json2csharp.com/" then we can Deserialize Json object into c#.
It will be something like this
string jsonString = "your Json Object as string";
var jsonObject = JsonConvert.DeserializeObject<RootObject>(jsonString);
foreach (JobProcessor obj in jsonObject.JobProcessors)
{
string JobName = obj.JobName;
bool value=obj.IsEnabled;
}
And I also converted given Json in c# object if the Json object is same you can directly use these classes.
public class Batching
{
public int BatchSize { get; set; }
public int DegreeOfParallelism { get; set; }
}
public class Settings
{
public int ArchivalJobCollectionPageSize { get; set; }
}
public class JobProcessor
{
public string JobName { get; set; }
public bool IsEnabled { get; set; }
public Batching Batching { get; set; }
public Settings Settings { get; set; }
}
public class Settings2
{
public string LeadTimeInSeconds { get; set; }
public int MaxSrsJobCount { get; set; }
}
public class PrimaryAction
{
public string ConnectionString { get; set; }
public Settings2 Settings { get; set; }
}
public class Settings3
{
public string LeadTimeInSeconds { get; set; }
public int MaxSrsJobCount { get; set; }
}
public class ErrorAction
{
public string ConnectionString { get; set; }
public string EntityPath { get; set; }
public Settings3 Settings { get; set; }
}
public class ScheduledJob
{
public string JobName { get; set; }
public PrimaryAction PrimaryAction { get; set; }
public ErrorAction ErrorAction { get; set; }
}
public class RootObject
{
public List<JobProcessor> JobProcessors { get; set; }
public List<ScheduledJob> ScheduledJobs { get; set; }
}
Hope this will help.
Thank you

Categories

Resources