Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JsonConvert dictionary with key of type Vector2Int

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;
}
like image 786
Seaky Avatar asked Oct 19 '25 02:10

Seaky


1 Answers

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());
like image 165
Programmer Avatar answered Oct 21 '25 16:10

Programmer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!