I am developing a game which has a class called UserInfo
.
Inside the UserInfo
class there is a public List<Lineup> lineups
variable.
And Lineup
contains public Dictionary<Vector2Int, Collection> cardLocations
.
My problem is, JsonConvert.SerializeObject
does correctly produce the result I want, but when I do JsonConvert.DeserializeObject<UserInfo>
, Vector2Int
remains to be a string like "(x, y)"
.
How should I fix it?
I wrote a JsonConverter
to convert it, and used
JsonConvert.SerializeObject(userInfo, Formatting.Indented, new Vec2Converter())
for serialization and
JsonConvert.DeserializeObject<UserInfo>(json, new Vec2Converter())
to deserialization, but it doesn't work.
Here is my JsonConverter
script:
using Newtonsoft.Json;
using UnityEngine;
using System;
public class Vec2Converter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var vector2Int = serializer.Deserialize<Vector2Int>(reader);
Debug.Log(vector2Int);
return vector2Int;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Vector2Int);
}
}
Another weird thing is that, when I tried to change Vector2Int
to other data structure like Lineup
or Dictionary<Vector2Int, Collection>
, Unity exited when I clicked play. After I changed it back, it became ok. The ok means it's not exiting but still giving me the error message.
Forgot to put the error message: ArgumentException: Could not cast or convert from System.String to UnityEngine.Vector2Int
Here are the classes.
public class UserInfo {
public string username;
public int playerID;
public List<Collection> collection = new List<Collection>();
public List<Lineup> lineups = new List<Lineup>(); // Here is the problem
public Dictionary<string, int> contracts = new Dictionary<string, int>();
public int coins = 0;
public int rank = 0;
public int lastLineupSelected = -1;
public int winsToday = 0;
public Stats total = new Stats();
public Dictionary<string, Stats> boardResults = new Dictionary<string, Stats>();
public List<Mission> missions;
public string preferredBoard = "Standard Board";
public string lastModeSelected = "";
public int gameID;
public bool missionSwitched = false;
}
public class Lineup
{
public Dictionary<Vector2Int, Collection> cardLocations; // Here is the problem
public List<Tactic> tactics;
public string boardName;
public string lineupName;
public string general;
public bool complete;
}
public class Collection
{
public string name = "";
public string type = "";
public int count = 1;
public int health = 0;
public int oreCost = 0;
}
Your custom serializer that derives from JsonConverter
needs more work and there are few errors. First, note that you are trying to serialize/de-serialize Dictionary
of Vector2Int
not just a Vector2Int
variable.
It's this:
public Dictionary<Vector2Int, Collection> cardLocations.
not
public Vector2Int cardLocations;
Because of the above statement, your override bool CanConvert(Type objectType)
function should be checking for typeof(Dictionary<Vector2Int, Collection>)
not typeof(Vector2Int)
. Also, you also need to add checks to determine when to run the custom de-serializer.
Serializing:
1.In the WriteJson
function that serializes the json, you only need to customize the Dictionary<Vector2Int, Collection>
part which is throwing the exception so make sure that the custom code runs only when the type is Dictionary<Vector2Int, Collection>
. You can do this by putting the code inside if (value is Dictionary<Vector2Int, Collection>)
.
2.Obtain the Dictionary to serialize from the second argument. Call writer.WriteStartArray()
so that you'll write every value as an array. Now, loop over the Dictionary
, write the key with writer.WriteValue(key)
then write the value with serializer.Serialize(writer, entry.Value);
.
3. After/outside the loop call writer.WriteStartArray();
then return from that function.
If the if (value is Dictionary<Vector2Int, Collection>)
from #1 is false
then simply call writer.WriteStartObject()
followed by writer.WriteEndObject()
.
De-Serializing:
4.Determine when to run the custom de-serializer
We used if (value is Dictionary<Vector2Int, Collection>)
in #1 when serializing to determine if the custom serializer code should run but for de-serializing, we use if (reader.TokenType == JsonToken.StartArray)
to determine if we have reached the array part where we did our custom serialization in the ReadJson
function.
5.Use JArray.Load(reader);
to obtain the array data we serialized. In the returned array, the first element in it is the key in the Dictionary
.The second element is the value in the Dictionary
. The third element is the second key in the Dictionary
. The fourth element is the second value in the Dictionary
ans so on.
6.Separate the keys and values in the JArray
.
To simplify this, loop over the JArray
increment by 2
instead of 1
. By incrementing by 2
, the key can be easily be retrieved with JArray[loop + 0]
and the value can be retrieved in the-same loop with JArray[loop + 1]
.
for (int i = 0; i < jArray.Count; i += 2)
{
//Key
string key = jArray[i + 0].ToString();
//Value
string value = jArray[i + 1].ToString();
}
7.Get the key in Vector2Int
format.
Simply de-serialize the key from #6 into Vector2Int
with JsonConvert.DeserializeObject<Vector2Int>(key)
.
8.Get the value in Collection
format.
Simply de-serialize the value from #6 into Collection
with JsonConvert.DeserializeObject<Collection>(value)
.
9.Reconstruct the data
Create new instance of Dictionary<Vector2Int, Collection>
then add both the key from #7 and value from #8 to it and then return it from the ReadJson
function.
If the if (reader.TokenType == JsonToken.StartArray)
from #4 is false
, simply create and return new instance of Dictionary<Vector2Int, Collection>
from the ReadJson
function without adding key or value to it.
class Vec2DictionaryConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Dictionary<Vector2Int, Collection>).IsAssignableFrom(objectType);
}
//Deserialize json to an Object
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//Debug.Log("De-serializing!");
if (reader.TokenType == JsonToken.StartArray)
{
// Load JArray from stream
JArray jArray = JArray.Load(reader);
//Where to re-create the json data into
Dictionary<Vector2Int, Collection> dict = new Dictionary<Vector2Int, Collection>();
if (jArray == null || jArray.Count < 2)
{
return dict;
}
//Do the loop faster with +=2
for (int i = 0; i < jArray.Count; i += 2)
{
//first item = key
string firstData = jArray[i + 0].ToString();
//second item = value
string secondData = jArray[i + 1].ToString();
//Create Vector2Int key data
Vector2Int vect = JsonConvert.DeserializeObject<Vector2Int>(firstData);
//Create Collection value data
Collection values = JsonConvert.DeserializeObject<Collection>(secondData);
//Add both Key and Value to the Dictionary if key doesnt exit yet
if (!dict.ContainsKey(vect))
dict.Add(vect, values);
}
//Return the Dictionary result
return dict;
}
return new Dictionary<Vector2Int, Collection>();
}
//SerializeObject to Json
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//Debug.Log("Serializing!");
if (value is Dictionary<Vector2Int, Collection>)
{
//Get the Data to serialize
Dictionary<Vector2Int, Collection> dict = (Dictionary<Vector2Int, Collection>)value;
//Loop over the Dictionary array and write each one
writer.WriteStartArray();
foreach (KeyValuePair<Vector2Int, Collection> entry in dict)
{
//Write Key (Vector)
serializer.Serialize(writer, entry.Key);
//Write Value (Collection)
serializer.Serialize(writer, entry.Value);
}
writer.WriteEndArray();
return;
}
writer.WriteStartObject();
writer.WriteEndObject();
}
}
Usage:
Serialize
string json = JsonConvert.SerializeObject(userInfo, new Vec2DictionaryConverter());
De-serialize
UserInfo obj = JsonConvert.DeserializeObject<UserInfo>(json, new Vec2DictionaryConverter());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With