Deserialize JSON into a sublevel object - c#

Suppose I have this JSON:
{
"Success": true,
"Records": [
{
"f_EMail": "test#me.com",
"f_FirstName": "firstname",
"f_LastName": "lastname",
"f_Country": null
},
{
"f_EMail": "test2#me.com",
"f_FirstName": "firstname2",
"f_LastName": "lastname2",
"f_Country": null
}
]
}
My class looks like this:
public class Result
{
public bool Success { get; set; }
public IEnumrable<Dictionary<string, string>> Records { get; set; }
}
Everything works as expected. But I would like to write my class a little bit differently and put the values inside Record.Data as shown below. I need to find a way to read and write to this model because some values are well known and I would like to access them more directly.
public class Result
{
public bool Success { get; set; }
public IEnumerable<Record> Records { get; set; }
}
public class Record
{
public Dictionary<string, string> Data { get; set; }
public string Email
{
get
{
return Data[KnownRecordField.Email];
}
}
public string FirstName
{
get
{
return Data[KnownRecordField.FirstName];
}
}
...
}
How can I do that?

If you are willing to declare your dictionary as Dictionary<string, object> instead of Dictionary<string, string> you can take advantage of Json.Net's "Extension Data" feature to handle this.
Mark the dictionary with an [JsonExtensionData] attribute.
Make properties for all of the well-known values and give them [JsonProperty] attributes corresponding to their JSON property names.
The well-known JSON properties will be deserialized into their respective members on the class, while all of the remaining values will go into the dictionary.
public class Record
{
[JsonExtensionData]
public Dictionary<string, object> Data { get; set; }
[JsonProperty("f_EMail")]
public string Email { get; set; }
[JsonProperty("f_FirstName")]
public string FirstName { get; set; }
...
}
Fiddle: https://dotnetfiddle.net/hGZ1V7
If you want all the values to go into the dictionary (not just the unknown ones), you can still use the [JsonExtensionData] attribute, then use separate properties to read and write the well-known values directly from the dictionary. Be sure to mark the properties with [JsonIgnore] to avoid potential conflicts during serialization.
public class Record
{
[JsonExtensionData]
public Dictionary<string, object> Data { get; set; }
[JsonIgnore]
public string Email
{
get { return GetDataValue(KnownRecordField.Email); }
set { Data[KnownRecordField.Email] = value; }
}
[JsonIgnore]
public string FirstName
{
get { return GetDataValue(KnownRecordField.FirstName); }
set { Data[KnownRecordField.FirstName] = value; }
}
// use this method to avoid an exception if the well-known value
// isn't present in the dictionary
private string GetDataValue(string key)
{
object value;
return Data.TryGetValue(key, out value) && value != null ? value.ToString() : null;
}
}
public static class KnownRecordField
{
public static readonly string Email = "f_EMail";
public static readonly string FirstName = "f_FirstName";
}
Fiddle: https://dotnetfiddle.net/I04oMM

you can add JsonProperty attribute in record class.
public class Record
{
public Dictionary<string, string> Data { get; set; }
[JsonProperty(PropertyName = "f_EMail")]
public string Email
{
get;set;
}
...
}

Related

How to use a placeholder within an if statement to access a property

I don't want to use lots of if statements to determine which property needs to be accessed and instead wondered if something like a placeholder could be used.
I attempted to code something with placeholders and this is where I encounted my problem.
if (rootObject.permissions.{commandGroup}.{commandName})
{
//do something
}
This would allow the property accessed to change based on the value of the string in commandGroup and commandName without having to use several if statements for when the JSON gets expanded.
Here is the problem if statement:
//Command is an instance of CommandInfo from Discord.Net
string commandGroup = command.Module.Group;
string commandName = command.Name;
if (rootObject.permissions.commandGroup.commandName)
{
//do something
}
Here is how the JSON file is being stored in a class:
internal class RootObject
{
public Permissions permissions { get; set; }
public int points { get; set; }
}
internal class Permissions
{
public Response response { get; set; }
}
internal class Response
{
public bool ping { get; set; }
public bool helloWorld { get; set; }
}
For example, if the commandGroup was Response and the commandName was ping, how would I use an if statement to determine if the value stored in rootObject.permissions.response.ping.
You could do it using reflection, like this:
public static class PermissionsExtensions {
public static T CommandGroup<T>(this Permissions permissions, string commandGroup)
{
PropertyInfo commandGroupProperty = typeof(Permissions).GetProperty(commandGroup);
return (T)(commandGroupProperty.GetValue( permissions));
}
public static bool CommandProperty<T>(this T commandGroup, string commandProperty)
{
PropertyInfo commandPropertyProperty = typeof(T).GetProperty( commandProperty);
return (bool)(commandPropertyProperty.GetValue( commandGroup));
}
}
Then you would use it like so:
bool result = rootObject.permissions.CommandGroup<Response>( "response").CommandProperty( "ping");
Tip: use capitalized names for properties in classes, and lower-case names for parameters.
It seems like the properties you want to access are all bool. You can store all of them in a Dictionary<string, bool>:
internal class RootObject
{
public Dictionary<string, bool> permissions { get; set; } = new Dictionary<string, bool> {
{ "response.ping", false },
{ "response.helloworld", false },
// add more here...
};
public int points { get; set; }
}
Now you you can access the dictionary like this:
if (rootObject.permissions[$"{commandGroup}.{commandName}"])

Fail to serialize IConfigurationSection from Json

I have following Json-based configuration file:
{
"PostProcessing": {
"ValidationHandlerConfiguration": {
"MinimumTrustLevel": 80,
"MinimumMatchingTrustLevel": 75
},
"MatchingCharacterRemovals": [
"-",
"''",
":"
]
},
"Processing": {
"OrderSelection": {
"SelectionDaysInterval": 30,
"SelectionDaysMaximum": 365
}
}
}
As serialization framework I use Newtonsoft. To serialize this config into objects I have implemented following classes:
[JsonObject(MemberSerialization.OptIn)]
public class RecognitionConfiguration {
[JsonProperty(PropertyName = "PostProcessing", Required = Required.Always)]
public PostRecognitionConfiguration PostRecognitionConfiguration { get; set; }
[JsonProperty(PropertyName = "Processing", Required = Required.Always)]
public ProcessRecognitionConfiguration ProcessRecognitionConfiguration { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class PostRecognitionConfiguration {
[JsonProperty(Required = Required.Always)]
public ValidationHandlerConfiguration ValidationHandlerConfiguration { get; set; }
[JsonProperty] public List<string> MatchingCharacterRemovals { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class ProcessRecognitionConfiguration {
[JsonProperty(PropertyName = "OrderSelection", Required = Required.Always)]
public OrderSelectionConfiguration OrderSelectionConfiguration { get; set; }
}
In a class I try to serialize a specific configuration section into these class structures using IConfigurationSection.Get().
var serializedConfiguration = this.ConfigurationSection.Get<RecognitionConfiguration>();
But when I debug the code, I always get an "empty" variable serializedConfiguration which is not null, but all properties are null.
If I use
this.ConfigurationSection.GetSection("Processing").Get<ProcessRecognitionConfiguration>()
or change the naming of the properties in the json file to exactly match the property names in the classes like this:
{
"ProcessRecognitionConfiguration": {
"OrderSelectionConfiguration": {
"SelectionDaysInterval": 30,
"SelectionDaysMaximum": 365
}
}
}
it it works fine. Do you have any idea, why setting PropertyName on JsonProperty does not seem to have any effect?
That is by design. Binding to POCO via configuration is done by convention. Not like Model Binding to Controller Action parameters.
It matches property names on the POCO to keys in the provided JSON.
Reference Configuration in ASP.NET Core
So either you change the settings to match the class like you showed in the original question, or change the class to match the settings keys in the Json-based configuration file.
[JsonObject(MemberSerialization.OptIn)]
public class RecognitionConfiguration {
[JsonProperty(PropertyName = "PostProcessing", Required = Required.Always)]
public PostRecognitionConfiguration PostProcessing{ get; set; }
[JsonProperty(PropertyName = "Processing", Required = Required.Always)]
public ProcessRecognitionConfiguration Processing{ get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class PostRecognitionConfiguration {
[JsonProperty(Required = Required.Always)]
public ValidationHandlerConfiguration ValidationHandlerConfiguration { get; set; }
[JsonProperty]
public List<string> MatchingCharacterRemovals { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class ProcessRecognitionConfiguration {
[JsonProperty(PropertyName = "OrderSelection", Required = Required.Always)]
public OrderSelectionConfiguration OrderSelection { get; set; }
}
public partial class ValidationHandlerConfiguration {
[JsonProperty("MinimumTrustLevel")]
public long MinimumTrustLevel { get; set; }
[JsonProperty("MinimumMatchingTrustLevel")]
public long MinimumMatchingTrustLevel { get; set; }
}
public partial class OrderSelectionConfiguration {
[JsonProperty("SelectionDaysInterval")]
public long SelectionDaysInterval { get; set; }
[JsonProperty("SelectionDaysMaximum")]
public long SelectionDaysMaximum { get; set; }
}
EDIT: I found this one is much more pleasant than my previous solutions: Bind everything in an ExpandoObject, write them to JSON and use JSON.NET to bind them back. Using the code of this article:
namespace Microsoft.Extensions.Configuration
{
public static class ConfigurationBinder
{
public static void BindJsonNet(this IConfiguration config, object instance)
{
var obj = BindToExpandoObject(config);
var jsonText = JsonConvert.SerializeObject(obj);
JsonConvert.PopulateObject(jsonText, instance);
}
private static ExpandoObject BindToExpandoObject(IConfiguration config)
{
var result = new ExpandoObject();
// retrieve all keys from your settings
var configs = config.AsEnumerable();
foreach (var kvp in configs)
{
var parent = result as IDictionary<string, object>;
var path = kvp.Key.Split(':');
// create or retrieve the hierarchy (keep last path item for later)
var i = 0;
for (i = 0; i < path.Length - 1; i++)
{
if (!parent.ContainsKey(path[i]))
{
parent.Add(path[i], new ExpandoObject());
}
parent = parent[path[i]] as IDictionary<string, object>;
}
if (kvp.Value == null)
continue;
// add the value to the parent
// note: in case of an array, key will be an integer and will be dealt with later
var key = path[i];
parent.Add(key, kvp.Value);
}
// at this stage, all arrays are seen as dictionaries with integer keys
ReplaceWithArray(null, null, result);
return result;
}
private static void ReplaceWithArray(ExpandoObject parent, string key, ExpandoObject input)
{
if (input == null)
return;
var dict = input as IDictionary<string, object>;
var keys = dict.Keys.ToArray();
// it's an array if all keys are integers
if (keys.All(k => int.TryParse(k, out var dummy)))
{
var array = new object[keys.Length];
foreach (var kvp in dict)
{
array[int.Parse(kvp.Key)] = kvp.Value;
}
var parentDict = parent as IDictionary<string, object>;
parentDict.Remove(key);
parentDict.Add(key, array);
}
else
{
foreach (var childKey in dict.Keys.ToList())
{
ReplaceWithArray(input, childKey, dict[childKey] as ExpandoObject);
}
}
}
}
}
Usage:
var settings = new MySettings();
this.Configuration.BindJsonNet(settings);
Here is my testing MySettings class:
public class MySettings
{
[JsonProperty("PostProcessing")]
public SomeNameElseSettings SomenameElse { get; set; }
public class SomeNameElseSettings
{
[JsonProperty("ValidationHandlerConfiguration")]
public ValidationHandlerConfigurationSettings WhateverNameYouWant { get; set; }
public class ValidationHandlerConfigurationSettings
{
[JsonProperty("MinimumTrustLevel")]
public int MinimumTrustLevelFoo { get; set; }
[JsonProperty("MinimumMatchingTrustLevel")]
public int MinimumMatchingTrustLevelBar { get; set; }
}
}
}
After the calling, I get everything as you desired:
Old Answer:
According to the source code here, it is simply (near) impossible to do what you are requiring. I have tried both JsonProperty and DataContract, none of which are honored by the Binder, simply because the source code itself simply use the property name.
If you still insist, there are 2 possibilities, however I do not recommend any as changing properties' names are much simpler:
Fork your source code there, or simply copy that file (in my attempt to trace the code, I rename all methods to something like Bind2, BindInstance2 etc), and rewrite the code accordingly.
This one is very specific to current implementation, so it's not future-proof: the current code is calling config.GetSection(property.Name), so you can write your own IConfiguration and provide your own name for GetSection method and tap it into the bootstrap process instead of using the default one.
Changing PropertyName on JsonProperty does have effect. Here is the same I tried and it did worked for me:
my JSON data:
{"name": "John","age": 30,"cars": [ "Ford", "BMW", "Fiat" ]}
and the Model:
public class RootObject
{
[JsonProperty(PropertyName ="name")]
public string Apple { get; set; }
public int age { get; set; }
public List<string> cars { get; set; }
}
and here is the code:
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
and this is the output i get
You need to set the PropertyName in JsonProperty same as json file property name but your C# model property can be what you wanted, just that they need to be decorated with [JsonProperty(PropertyName ="jsonPropertyName")] Hope this helps you solve your issue.
Happy coding...

JArray to c# Object

I have this json string:
[
{
"id":"EORDERING_GRE017",
"name":"DELIMITER",
"value":"|"
},
{
"id":"EORDERING_GRE017",
"name":"ENABLED",
"value":"Y"
},
{
"id":"EORDERING_GRE017",
"name":"EXTERNALERRORRECIPIENT",
"value":"jaymie#moo.co.uk; matt#moo.co.uk"
},
{
"id":"EORDERING_GRE017",
"name":"FILETYPE",
"value":"delimited"
},
{
"id":"EORDERING_GRE017",
"name":"INTERNALERRORRECIPIENT",
"value":"jaymie#moo.co.uk; matt#moo.co.uk"
},
{
"id":"EORDERING_GRE017",
"name":"USESOWNBRANCHCODES",
"value":"True"
},
{
"id":"EORDERING_GRE017",
"name":"USESOWNSKUS",
"value":"True"
}
]
And I would like to turn that json into my class, which looks like this:
public class Settings
{
public bool Enabled { get; set; }
public string FileType { get; set; }
public string Delimiter { get; set; }
public string OrderFileSuffix { get; set; }
public string ResultFileSuffix { get; set; }
public bool UseOwnBranchCodes { get; set; }
public bool UseOwnProductCodes { get; set; }
public string InternalContacts { get; set; }
public string ExternalContacts { get; set; }
}
But I am unsure which is the best way to do this. Can someone give me a hand? :)
You could create a NameValue object:
public class NameValuePair
{
public string Name { get; set; }
public string Value { get; set; }
}
And deserialize the json array to List and convert it to Dictionary:
var dict = JsonConvert.DeserializeObject<List<NameValuePair>>(json).ToDictionary(x => x.Name, x => x.Value);
Then create a custom converter class with a method that accepts this dictionary and returns a Settings object:
public class SettingsConverter
{
public Settings Convert(IDictionary<string, string> data)
{
return new Settings
{
Enabled = data["ENABLED"].Equals("Y", StringComparison.Ordinal),
...
};
}
}
The JSON is an array of objects that would fit this class:
public class Setting
{
public string Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
So deserialize into that:
var settingList = JsonConvert.DeserializeObject<Setting[]>(jsonString);
But then you want to map specific settings to specific properties of that Settings class. You could do that by trying to find the particular setting for each property in the list of settings:
var settingsObject = new Settings
{
FileType = settingList.FirstOrDefault(s => s.Name == "FILETYPE")?.Value,
Delimiter = settingList.FirstOrDefault(s => s.Name == "DELIMITER")?.Value,
// ...
}
You'll have to manually map the properties. If you don't want that, you could write a custom serializer, but that'll quickly become a maintenance nightmare.
For those out there that may need some more help with the JSON Class Configuration, try: http://json2csharp.com/#
An excellent way of Auto Generating the Classes!
Or even easier, in VS, Goto:
Edit -> Paste Special -> Paste as JSON Classes

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

Derserialize JSON Object from Firebase in C#

I am querying Firebase and retrieve a collection of objects like so:
{"-K5f0ccEKkVkxTAavQKY": {
"Appeal": {
"ID": "1450273330435",
"comps": [
162248,
162272,
162273,
162281,
162544
],
"property": {
"Address": "15 Main Street",
"propID": 169729
},
"timeDateStamp": "Wed Dec 16 2015 08:42:10 GMT-0500 (Eastern Standard Time)",
"userUUID": "google:229139952703238437512",
"year": 2016
}
}}
I would like to deserialize them into objects with this definition:
public class Appeal
{
public string ID;
public List<string> comps;
public AppealProperty property;
public string timeDateStamp;
public string UUID;
public int year;
}
public class AppealProperty
{
public string address;
public string propID;
}
I have troubles getting it deserialized. I don't need the initial string (e.g. "K5f0ccEKkVkxTAavQKY"). I'm able to change the object definitions if need be. I have a feeling a Dictionary would be useful.
The quick and dirty object is to use Dictionary<string,Appeal> as your deserialization target. At that point it would be as simple as:
var firebaseLookup = JsonConvert.DeserializeObject<Dictionary<string,Appeal>>(json);
var data = firebaseLookup.Values.ToList(); // or FirstOrDefault();
This approach would also handle the case if you ever had to get multiple objects at once, and it would give you the opportunity to use that key if it turns out the key was important after all.
You could serialise your data into the classes below.
public class AppealProperty
{
public string Address { get; set; }
public int propID { get; set; }
}
public class Appeal
{
public string ID { get; set; }
public List<int> comps { get; set; }
public AppealProperty property { get; set; }
public string timeDateStamp { get; set; }
public string userUUID { get; set; }
public int year { get; set; }
}
public class FireBase
{
public Appeal Appeal { get; set; }
}
public class RootObject
{
[JsonProperty(PropertyName = " - K5f0ccEKkVkxTAavQKY")]
public FireBase FireBaseRoot
{
get;
set;
}
}
Assuming that you are using JSON.NET, you can then get the object you are after, using this snippet:
var firebaseObject = JsonConvert.DeserializeObject<RootObject>(json);
var data = firebaseObject.FireBaseRoot.Appeal;
If the root name is dynamic, as indicated by your comment, you could skip the root instead and serialise straight into the FireBase class:
JObject parsedJson = JObject.Parse(json);
var fireBase = parsedJson.First.Children().First().ToObject(typeof (FireBase));
Since I've never been able to parse a DataSnapshot with newtonSoft Json parser, I did this to build a list of object I needed to put in a ListView:
MyModelObject class
public class MyModelObject: Java.Lang.Object
{
public string Title { get; set; }
public string Description { get; set; }
public MyModelObject(){}
}
into My Listener
public void OnDataChange(DataSnapshot snapshot)
{
List<MyModelObjecct> myList = new List<MyModelObject>();
myList = databaseService
.GetMyModelObjectList(snapshot
.Children?
.ToEnumerable<DataSnapshot>());
}
Method into the DatabaseService class
public List<MyModelObject> GetMyModelObjectList(IEnumerable<DataSnapshot> enumerableSnapshot)
{
List<MyModelObject> list = new List<MyModelObject>();
foreach (var item in enumerableSnapshot)
{
list.Add(ObjectExtensions.DataSnapshotToObject<MyModelObject>(item.Children?.ToEnumerable<DataSnapshot>()));
}
return list;
}
ObjectExtensions class
public static class ObjectExtensions
{
public static T DataSnapshotToObject<T>(IEnumerable<DataSnapshot> source)
where T : class, new()
{
var someObject = new T();
var someObjectType = someObject.GetType();
foreach (var item in source)
{
someObjectType
.GetProperty(item.Key)
.SetValue(someObject, item.Value.ToString(), null);
}
return someObject;
}
}

Categories

Resources