I am trying to make a class that can save and load its own members from a json file automatically.
What I want to know is if NewtonSoft.Json provides a method to do that already or if I will have to use reflection.
class Settings
{
// this is my setting
public bool dostuff = false;
public int maxstuff = 123;
public string namestuff = "foo";
List<string> arrayofstuff = new List<string>();
private string fileLocation;
public Settings(string fileLocation)
{
this.fileLocation = fileLocation;
}
public void LoadSettings()
{
string content = System.IO.File.ReadAllText(this.fileLocation);
JObject data = JObject.Parse(content);
// Normally I would have a sub class that contains all the settings
// I would create an instance of it. Serialize into a JObject
// Then merge with the data object.
// Then use ToObject to assign the updated values
myDuplicateJObject.Merge(data, new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
});
// However I need to apply it to the current object which is "this"
}
public void SaveSettings()
{
System.IO.File.WriteAllText(this.fileLocation, JsonConvert.SerializeObject(this));
}
}
The only two ways I can currently think of to solve this would be to use reflection to try and merge a duplicate copy of my class or to make a sub class that contains all the settings and just use that as a member.
You can use JsonConvert.PopulateObject, passing this into it.
public class Settings
{
public string Name = "foo";
public void Populate(string json)
{
JsonConvert.PopulateObject(json, this);
}
}
You can change that Populate method to read the JSON file itself.
Related
I've made a class containing everything from the json file.
Now I want to loop trough every object within this object so I can for fill the application with those values.
I want to scroll trough every CM0xx and use that description:
I hope my goal is clear with that screenshot.
I know that I have to do something like
foreach(CM0XX step in Stepsss)
but that simply wont work.
If making a list<string[]> for it is easier from a json but im clueless for the solution.
this is the code I have now. And the json file ive converted is generated so that should be fine.
string testdata = File.ReadAllText(Application.StartupPath + "\\TestData\\SSH.json");
Root JsonDecoded = JsonConvert.DeserializeObject<Root>(testdata);
testSteps Stepsss = JsonDecoded.SmartShoulder.TestSteps;
foreach (testSteps step in Stepsss)
{
}
this is part of the json. there are alot more CM012
public class CM181
{
public string Name;
public string description;
public string minValue;
public string maxValue;
public string unit;
public string instructions;
public string prerequisites;
}
public class Root
{
public kkkk kk;
}
public class kkkk
{
public string preset;
public testSteps TestSteps;
}
public class testSteps
{
public CM011 CM011;
}
You could use reflection to loop through the names and the values. If you just need the items under TestSteps, this should work. If you need a full hierarchy, that is a bit more involved. Here is an example below and the fiddle to it:
using System;
using System.Reflection;
public class Program
{
public static void Main()
{
var record = new Root()
{
Foo = "foo",
Bar = "bar"
};
PropertyInfo[] rootProperties = typeof(Record).GetProperties();
foreach (PropertyInfo property in rootProperties)
{
var value = property.GetValue(record);
Console.WriteLine(property.Name + " - " + value);
}
}
}
public class Root
{
public string Foo {get;set;}
public string Bar {get;set;}
}
Making a class to deserialize to is generally done to avoid the need to loop through all the properties. You've defined all the properties in the class, so you can just use them. It's a lot of overhead to use reflection to put all the values into a class, then again using reflection to pull them back out. That's why Newtonsoft has JToken/JObject/JArray classes.
using Newtonsoft.Json.Linq;
...
// parse your JSON
var jo = JObject.Parse(jsonString);
// go through all properties
// just for an example, I put them all in a dictionary
var dict = new Dictionary<String, JToken>()
foreach( JProperty p in jo.Properties() ) {
dict.Add(p.Name, p.Value)
}
Now, each Value is a JToken, which could actually be another JObject, or a JArray or just a simple token. You can check JToken.Type to see what type the property really has, and do something logical accordingly.
I have multiple JSON files with data and multiple classes with properties mapped to fields in the JSON files. I have a generic method for loading the JSON like this:
public static TResult LoadFromJSON<TResult>(string pathToJSON)
{
using (var reader = new StreamReader(pathToJSON))
{
var json = reader.ReadToEnd();
var records = JsonConvert.DeserializeObject<TResult>(json);
return records;
}
}
and then another method that passes in the Class object I am targeting into the above LoadFromJSON
public SortingData GetJSONTestData()
{
var pathJSON = #"..\..\TestData\sortData.json";
var sortCriteriaJSON = TestDataLoader.LoadFromJSON<SortingData>(pathJSON);
return sortCriteriaJSON;
}
what I want to do is make that second method generic, so instead of having SortingData class (in 2 places) and sortData.json file, I can pass in the Class (in both places) and the JSON file dynamically..... so I don't have to duplicate the method over and over just for a different class. So something like this:
public <dynamicObject> GetJSONTestData()
{
var pathJSON = #"..\..\TestData\<dynamic json file>";
var sortCriteriaJSON = TestDataLoader.LoadFromJSON<dynamicObject>(pathJSON);
return sortCriteriaJSON;
}
Not sure how to go about this. Any ideas?
You can use typeof and string concatenation to achieve this, like so:
public T GetJsonTestData<T>()
{
// Say T is SortingData, name here is "SortingData"
var name = typeof(T).Name;
// After this, name is "sortingData"
name = Char.ToLowerInvariant(name[0]) + name.Substring(1);
var path = $#"..\..\TestData\{name}";
return LoadFromJson<T>(path);
}
Then you could call it like so:
// This would read "..\..\TestData\sortingData.json"
var sortingData = GetJsonTestData<SortingData>();
// This would read "..\..\TestData\fooBar.json"
var whatever = GetJsonTestData<FooBar>();
It sounds like you want to be able to map types to file names. You can either resolve the file names by convention, or load the mapping through some configuration.
In the example below the configuration is hard coded, but it wouldn't be much to load it from an appSettings file (demonstrating that is beyond the scope of this question). With the configuration, it's pretty trivial to get the file name from the dictionary and pass it to LoadFromJson.
public class TestDataLoader
{
private readonly string basePath = #"..\..\TestData";
private readonly Dictionary<Type, string> fileNameRegistry = new()
{
{ typeof(SortingData), "sorting_data.json" }
};
public TResult GetJSONTestData<TResult>()
{
var typefileName = fileNameRegistry[typeof(TResult)];
var path = Path.Combine(basePath, typeFileName);
return LoadFromJSON<TResult>(path);
}
public static TResult LoadFromJSON<TResult>(string pathToJSON)
{
using (var reader = new StreamReader(pathToJSON))
{
var json = reader.ReadToEnd();
var records = JsonConvert.DeserializeObject<TResult>(json);
return records;
}
}
}
I've used https://quicktype.io to create a class for my Json data. However I'm not really understanding why with the helper methods created to convert to json, from json, and supply default parameters they are each created differently as a static method of the class, a static class and a class with a static method.
The reason really is because as soon as I create a second class from another set of json data the code fails because of the static classes and I want to make sure I refactor them correctly. As well as understand the reasons of course.
I figure 'Converter' will never change across all my json objects so I can move this as is to a separate file and Serialize to a static method with FromJson. But I'd just like to understand more about the reasoning of how it was done in the first place and the better approach.
Here is the code:
public partial class StationDO
{
public string Active { get; set; }
//more fields here
}
public partial class StationDO
{
public static List<StationDO> FromJson(string json)
{
return JsonConvert.DeserializeObject<List<StationDO>>(json, Converter.Settings);
}
}
public static class Serialize
{
public static string ToJson(this List<StationDO> self)
{
return JsonConvert.SerializeObject(self, Converter.Settings);
}
}
public class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
};
}
If I look at the Java code the same site produces, it simply puts everything bar the bean into a single class 'Converter'
You can have all the static members in StationDO class. In that case I recommend mark that class as sealed (public sealed class StationDO) to prevent someone to inheriting from that class and use the static methods from inherited class.
public class InheritedStationDO : StationDO { }
// ... somewhere else ...
InheritedStationDO.FromJson(jsonValue); // still returns List<StationDO> not List<InheritedStationDO> !!!
EDIT:
After close look I think, the whole design of members is not good.
1) There is no need to accept just List<StationDO>.
2) There is no need the defined special methods for (de)serialization of every class, you will have. You can have just one method for serialization and one for deserialization for all your classes.
Example:
public class StationDO {
public string Active { get; set; }
}
public class AnotherDO {
public string Name { get; set; }
}
// and more *DO classes
// class need to be "static" because contains "extension methods"
public static class MySerializationHelper {
private static readonly JsonSerializerSettings serializationSettings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
};
// universal method for deserialization from json
// the generic type "T" represents the result type of deserialization
public static T FromJson<T>(string json) {
return JsonConvert.DeserializeObject<T>(json, serializationSettings);
}
// universal method for serialization to json
// this "this" keyword means, its "extension method"
public static string ToJson<T>(this T self) {
return JsonConvert.SerializeObject(self, serializationSettings);
}
}
Usage:
StationDO obj01 = GetSomeStation();
// returns json of one object
string json01A = obj01.ToJson(); // these two lines are equivalent
string json01B = MySerializationHelper.ToJson(obj01); // these two lines are equivalent
// returns new object deserialized from json in "json01" variable
StationDO clone01 = MySerializationHelper.FromJson<StationDO>(json01A);
StationDO obj02 = GetAnotherStation();
StationDO[] arr01 = new StationDO[] { obj01, obj02 };
// returns json of array with two objects
string json02A = arr01.ToJson(); // these two lines are equivalent
string json02B = MySerializationHelper.ToJson(arr01); // these two lines are equivalent
// returns new array with deserialized object from json in "json02" variable
StationdDO[] clone02 = MySerializationHelper.FromJson<StationdDO[]>(json02A);
AnotherDO obj03 = GetAnotherDO();
string json03A = obj03.ToJson(); // these two lines are equivalent
string json03B = MySerializationHelper.ToJson(obj03); // these two lines are equivalent
As you see, the generics is the way, how to avoid code duplication for every class.
And you can (de)serialize all kind of arrays and collections or single objects. Not just List<T>.
i want to use plugins to extend my c# wpf application. i've made a simple interface, then a plugin dll, then a test class to load the plugin. the plugin load properly and i can get a list of its properties.
the inteface:
public interface IStrat
{
string Name { get; }
void Init();
void Close(int dat);
}
the plugin:
public class Class1 : IStrat
{
public string info;
[Input("Info")]
public string Info
{
get
{
return info;
}
set
{
info = value;
}
}
public string Name
{
get { return "Test Strategy 1"; }
}
public void Init()
{
}
public void Close(int dat)
{
}
}
the test class:
class test
{
public void getPlugins()
{
Assembly myDll = Assembly.LoadFrom(Class1.dll);
var plugIn = myDll.GetTypes();
List<string> temp = new List<string>();
//gets the properties with "input" attribute, it returns the Info property fine
var props = item.GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(Input)));
foreach (var prop in props)
{
temp.Add(prop.Name + " (" + prop.PropertyType.Name + ")");// returns Info (string)
}
stratFields.Add(item.Name, temp);// stratFields is a dictionary that keeps the name of the plugin as key and a list of properties names as value
}
public void create()
{
//create an instance of my plugin
Type t = plugIn[0];
var myPlugin = (IStrat)Activator.CreateInstance(t);
myPlugin.Init(); // this works, i can access the methods
myPlugin.Info = "test"; //this doesn't work
}
}
i want to access the "Info" property to get/set it for that specific instance. when i use the getproperties() method it finds it, so there must be a way to use it.
different plugins have different number and type of properties.
Since Info property is not part of the interface you have to use reflection (Set object property using reflection) or dynamic:
myPlugin.Init(); // this works because IStrat has Init method
dynamic plugin = myPlugin;
plugin.Info = "test"; // this works because `Info` is public property and
// dynamic will perform late (run-time) binding.
Better approach would be add all necessary methods to the interface.
Currently I'm trying to utilize Json.Net serializer in my projects.
One of the problems that I've faced recentry is that deserialized object gets all of it's private list items duplicated.
It can be reproduced like this:
Create class with private field of List<> type, where List<> generic parameter is some other class
Add public property, that gets/sets private field
Create instance of that class, add 1 item to inner list, using property.
Serialize with new DefaultContractResolver that is setup to see private class data;
Deserialize
Code to reproduce the problem:
public class ClassParam
{
public int? ParamOne
{
get;
set;
}
}
public class ClassWithParams
{
private List<ClassParam> _privateFieid = new List<ClassParam>();
public List<ClassParam> PropertWithBackingField
{
get { return _privateFieid; }
set { _privateFieid = value; }
}
public void AddElementToPrivateField(ClassParam classParam)
{
_privateFieid.Add(classParam);
}
}
[Test]
public void Test()
{
var instance = new ClassWithParams();
var param1 = new ClassParam { ParamOne = 1 };
instance.PropertWithBackingField.Add(param1);
var contractResolver = new DefaultContractResolver();
contractResolver.DefaultMembersSearchFlags |= BindingFlags.NonPublic;
string serializedInstance = JsonConvert.SerializeObject(instance,
Formatting.Indented,
new JsonSerializerSettings()
{
ContractResolver = contractResolver
});
var deserializeInstance = JsonConvert.DeserializeObject(serializedInstance, typeof(ClassWithParams),
new JsonSerializerSettings()
{
ContractResolver = contractResolver
});
}
When I remove public property PropertWithBackingField from ClassWithParams it's all ok.
The problem is gone as well when I don't use custom setup for ContractResolver. But I do need to serialize private data of my classes as soon as not all of it is exposed via public properties.
What's wrong with my code? Are there any subtleties, using Json.Net or is it a bug?
For this code
var s = JsonConvert.SerializeObject(instance);
var desInst = JsonConvert.DeserializeObject<ClassWithParams>(s);
Your json will be {"PropertWithBackingField":[{"ParamOne":1}]}
But you say to include private field in serialization/deserialization with
contractResolver.DefaultMembersSearchFlags |= BindingFlags.NonPublic;
and get a json
{
"_privateFieid": [
{
"ParamOne": 1
}
],
"PropertWithBackingField": [
{
"ParamOne": 1
}
]
}
So there are two deserializations, one for _privateFieid and one for PropertWithBackingField
Either use BindingFlags.Public or use my code above with is much simpler.
DeserializeInstance method receives existing instance as argument. Probably it does not creates new instance but fills in existing and returns it.
Try to put ReferenceEquals(instance, deserializedInstance) to your watch. To avoid data duplicating use overload that does not accepts existing instance or create new instance and deserialize it.