I am building a generic save system for Unity. As serializer I use Json.NET.
The main data container is a class that holds a Dictionary<string/*dataKey*/, ISaveableData/*data to be saved*/>
. Because ISaveableData
is an interface, I use the concrete type as key in the json. This way I know into which type the data needs to be deserialized when loading.
Everything works fine up to the point where I try to deserialize the data into the concrete class. I created a custom converter to handle deserialization, but I seem to get something wrong.
My current Read()
method in the custom converter looks something like this:
public override Data ReadJson(JsonReader reader, Type objectType, Data existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer)
{
//load json object
JObject jObject = JObject.Load(reader);
Data data = new();
foreach (JToken token in jObject.Children())
{
//get key
string key = token.Path; //in my example 'key' will either be "DataTypeOne" or "DataTypeTwo"
//determine correct type, based on the key (pseudo-code, this step works fine)
Type type = GetTypeByKey(key);
//now I know the type, I want to deserialize the token into an instance of the specified type
//this fails with error 'Unexpected token while deserializing object: PropertyName. Path 'DataTypeOne'
object deserializedInstance = token.ToObject(type, serializer);
//add to data
data.Add((ISaveable)deserializedInstance);
}
return data;
}
The json looks something like this:
{
"SaveSlotName": "Example",
"Data": {
"DataTypeOne": {
"SomeValue": 5
},
"DataTypeTwo": {
"SomeOtherValue": "Hello"
}
}
}
Why does JToken.ToObject(type, serializer)
throw an error? How is the Read()
method supposed to be used?
I found the issue.
I tried to deserialize the token, when instead I need to deserialize the value of the token.
This is the new Read()
method. Maybe it's helpful for others running into the same issue.
public override Data ReadJson(JsonReader reader, Type objectType, Data existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer)
{
//load json object
JObject jObject = JObject.Load(reader);
Data data = new();
foreach (JToken token in jObject.Properties())
{
//get key
string key = token.Path; //in my example 'key' will either be "DataTypeOne" or "DataTypeTwo"
//determine correct type, based on the key (pseudo-code, this step works fine)
Type type = GetTypeByKey(key);
//get the value of this property
JToken value = property.Value;
//now I know the type and the property value and can deserialize it into an instance of the specified type
object deserializedInstance = value.ToObject(type, serializer);
//add to data
data.Add((ISaveable)deserializedInstance);
}
return data;
}