Reputation: 1114
I want so serialize/deserialize the following model:
public class ReadabilitySettings
{
public ReadabilitySettings() {
}
private bool _reababilityEnabled;
public bool ReadabilityEnabled {
get {
return _reababilityEnabled;
}
set {
_reababilityEnabled = value;
}
}
private string _fontName;
public string FontName {
get {
return _fontName;
}
set {
_fontName = value;
}
}
private bool _isInverted;
public bool IsInverted {
get {
return _isInverted;
}
set {
_isInverted = value;
}
}
public enum FontSizes
{
Small = 0,
Medium = 1,
Large = 2
}
private FontSizes _fontSize;
public FontSizes FontSize { get
{
return _fontSize;
}
set
{
_fontSize = value;
}
}
}
}
I have a list containing instances of the following object:
public class CacheItem<T>
{
public string Key { get; set; }
public T Value { get; set; }
}
I populate the list like so:
list.Add(new CacheItem<ReadabilitySettings>() { Key = "key1", Value = InstanceOfReadabilitySettings };
When I want to serialize this list I call:
var json = JsonConvert.SerializeObject (list);
This works fine. No errors. It gives the following json:
[{"Key":"readbilitySettings","Value":{"ReadabilityEnabled":true,"FontName":"Lora","IsInverted":true,"FontSize":2}}]
When I want to deserialize the list I call:
var list = JsonConvert.DeserializeObject<List<CacheItem<object>>> (json);
This gives me a list of CacheItem's with it's Value property set to a JObject. No errors so far.
When I want the actual instance of ReadabilitySettings I call:
var settings = JsonConvert.DeserializeObject<ReadabilitySettings> (cacheItem.Value.ToString ());
I have to call this since the cacheItem.Value is set to a json string, not to an instance of ReadabilitySettings. The json string is:
{{ "ReadabilityEnabled": true, "FontName": "Lora", "IsInverted": true, "FontSize": 2 }} Newtonsoft.Json.Linq.JObject
Then I get this error: "Error setting value to 'ReadabilityEnabled' on 'Reflect.Mobile.Shared.State.ReadabilitySettings'."
What am I missing? Thanks!
EDIT------
This is the method that throws the error:
public T Get<T> (string key)
{
var items = GetCacheItems (); // this get the initial deserialized list of CacheItems<object> with its value property set to a JObject
if (items == null)
throw new CacheKeyNotFoundException ();
var item = items.Find (q => q.Key == key);
if (item == null)
throw new CacheKeyNotFoundException ();
var result = JsonConvert.DeserializeObject<T> (item.Value.ToString ()); //this throws the error
return result;
}
Upvotes: 2
Views: 6705
Reputation: 1114
Managed to solve it. Sample code did not show that ReadabilitySettings also implemented the INotifyPropertyChanged interface. The subsequent eventhandler wired-up to the PropertyChanged event of ReadabilitySettings somewhere else in the project had some errors and thus the deserializer was not able to instantiate ReadabilitySettings :-).
Not a glamorous save but its working... Thanks for your time.
Upvotes: 0
Reputation: 2472
I've just tried this, which is pretty much copy/pasting your code and it works fine using Newtonsoft.Json v6.0.0.0 (from NuGet):
var list = new List<CacheItem<ReadabilitySettings>>();
list.Add(new CacheItem<ReadabilitySettings>() { Key = "key1", Value = new ReadabilitySettings() { FontName = "FontName", FontSize = ReadabilitySettings.FontSizes.Large, IsInverted = false, ReadabilityEnabled = true } });
var json = JsonConvert.SerializeObject(list);
var list2 = JsonConvert.DeserializeObject<List<CacheItem<object>>>(json);
var settings = JsonConvert.DeserializeObject<ReadabilitySettings>(list2.First().Value.ToString());
However, you don't need the last line. Simply switch List<CacheItem<object>>
to List<CacheItem<ReadabilitySettings>>
in your call to DeserializeObject
and it automatically resolves it:
var list2 = JsonConvert.DeserializeObject<List<CacheItem<ReadabilitySettings>>>(json);
Now list2.First().Value.GetType() = ReadabilitySettings
and there's no need to do any further deserialising. Is there a reason you're using object?
I'm not sure if this helps you necessarily, but given what you're trying to do have you thought about using a custom converter? I had to do this a few days ago for similar reasons. I had an enum property coming back in my JSON (similar to your key) which gave a hint as to what type a property in my JSON and therefore my deserialised class was. The property in my class was an interface rather than an object but the same principle applies for you.
I used a custom converter to automatically handle creating the object of the correct type cleanly.
Here's a CustomConverter for your scenario. If your key is set to readbilitySettings then result.Value
is initialised as ReadabilitySettings
.
public class CacheItemConverter : CustomCreationConverter<CacheItem<object>>
{
public override CacheItem<object> Create(Type objectType)
{
return new CacheItem<object>();
}
public CacheItem<object> Create(Type objectType, JObject jObject)
{
var keyProperty = jObject.Property("Key");
if (keyProperty == null)
throw new ArgumentException("Key missing.");
var result = new CacheItem<object>();
var keyValue = keyProperty.First.Value<string>();
if (keyValue.Equals("readbilitySettings", StringComparison.InvariantCultureIgnoreCase))
result.Value = new ReadabilitySettings();
else
throw new ArgumentException(string.Format("Unsupported key {0}", keyValue));
return result;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
var target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
/* Here your JSON is deserialised and mapped to your object */
return target;
}
}
Usage:
var list = JsonConvert.DeserializeObject<List<CacheItem<object>>>(json, new CacheItemConverter());
Here's a link to a full working example of it: http://pastebin.com/PJSvFDsT
Upvotes: 2