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