I have a type and I need to round-trip only selected private fields of that type to and from JSON, ignoring all other fields and properties whether public or private. How can I do that using Json.NET?
My current approach is to use an intermediate Dictionary<string, object>
. Serialization works, but deserialization does not
-
For serialization, I added a method to my type that constructs a
Dictionary<string, object>
and populates it with the required fields by name. Later, I serialize the dictionary.This works.
-
For deserialization, I deserialize to a
Dictionary<string, object>
and then attempt to populate the fields of my type by name and value using reflection.This does not work because the
object
value in the dictionary has the wrong type.
How can I fix this?
Sample class:
public partial class MyClass
{
// Parameters is a instance of class which is created for storing some values.
private Dictionary<string, Parameters> SomeParameters;
private NotificationList notifications ;
private string someKey;
private int someNumber;
private char aChar;
public MyClass() { }
public MyClass(Dictionary<string, Parameters> someParameters, NotificationList notifications, string someKey, int someNumber, char aChar) =>
(this.SomeParameters, this.notifications, this.someKey, this.someNumber, this.aChar) = (someParameters, notifications, someKey, someNumber, aChar);
//...
//other properties and fields which should not be [de]serialized for this specific operation.
public string IgnoreMe { get; set; } = "ignore me";
private string alsoIgnoreMe = "ignore me";
}
public record Parameters(string Key, string Value);
public class NotificationList : List<string> { }
Sample class methods used to create my dictionary, and recreate MyClass
from a dictionary:
public partial class MyClass
{
public Dictionary<string, object> ToDictionary() => new()
{
{ nameof(SomeParameters), SomeParameters},
{ nameof(notifications), notifications },
{ nameof(someKey), someKey },
{ nameof(someNumber), someNumber },
{ nameof(aChar), aChar },
};
public static MyClass FromDictionary(Dictionary<string, object> settings)
{
var instance = new MyClass();
foreach (string settingValue in settings.Keys)
{
Type type = typeof(Dictionary<string, object>);
FieldInfo? fieldInfo = type.GetField(settingValue, BindingFlags.NonPublic | BindingFlags.Instance);
// This does not work -- fieldInfo is null, and settings[settingValue] is not the correct type.
fieldInfo?.SetValue(instance, settings[settingValue]);
}
return instance;
}
}
Sample JSON:
{
"SomeParameters": {
"name": {
"Key": "hello",
"Value": "There"
}
},
"notifications": [],
"someKey": "some key",
"someNumber": 101,
"aChar": "z"
}
So key(string) is my field name and object is the field value itself. There is no problem with serializing but I cannot deserialize back the object
(value) to original type. I searched a lot and could not find any simple solution.
NOTE: I cannot put these variables inside a class and deserialize back. Especially i need to do this operation inside that class and set the values back. It must be done for every field separately.
Demo fiddle here.
2
Answers
Your
FromDictionary()
method needs two fixes:When you do
type.GetField(settingValue, ...)
the type you use must betypeof(MyClass)
nottypeof(Dictionary<string, object>)
.Before setting the field value, you must deserialize or convert the dictionary value to the type of the field.
This can be done as follows:
Using the following extension method:
Demo fiddle #1 here.
As an alternative, if you need to serialize and deserialize only specific (possibly private) fields of some type, you can do this directly with a custom contract resolver. Using one completely eliminates the need for some intermediate
Dictionary<string, object>
and so avoids the need to somehow guess the type of the value objects when deserializing.First define the following resolver:
Then, modify
MyClass
as follows:And now you will be able to round-trip your selected fields to and from JSON as follows:
Demo fiddle #2 here.
your code is very slopy. Just use this FromDictionary code
and mark your constructor with parameters as [JsonConstructor]
and IMHO
I have never seen any good from collection inheritance