Словарь JsonConvert с ключом типа Vector2Int - PullRequest
0 голосов
/ 26 мая 2018

Я занимаюсь разработкой игры, класс которой называется UserInfo.

Внутри класса UserInfo есть переменная public List<Lineup> lineups.

И Lineup содержит public Dictionary<Vector2Int, Collection> cardLocations.

Моя проблема в том, что JsonConvert.SerializeObject правильно создает желаемый ввод, но когда я делаю JsonConvert.DeserializeObject<UserInfo>, Vector2Int остается строкой, подобной "(x, y)".

Как это исправить?

Я написал JsonConverter для преобразования и использовал

JsonConvert.SerializeObject(userInfo, Formatting.Indented, new Vec2Converter()) 

для сериализации и

JsonConvert.DeserializeObject<UserInfo>(json, new Vec2Converter())

десериализовать, но это не работает.

Вот мой JsonConverter скрипт:

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);
    }
}

Еще одна странная вещь, когда я пытался изменить Vector2Int на другую структуру данныхкак Lineup или Dictionary<Vector2Int, Collection>, когда я нажал кнопку воспроизведения, Unity вышла из игры.После того, как я изменил его обратно, все стало хорошо.ОК означает, что он не завершается, но все равно выдает мне сообщение об ошибке.

Забыл поставить сообщение об ошибке: ArgumentException: Could not cast or convert from System.String to UnityEngine.Vector2Int

Вот классы.

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;
}

Ответы [ 2 ]

0 голосов
/ 28 мая 2018

Ваш пользовательский сериализатор, производный от JsonConverter, требует доработки и содержит несколько ошибок.Во-первых, обратите внимание, что вы пытаетесь сериализовать / десериализовать Dictionary из Vector2Int, а не просто переменную Vector2Int.

Это так:

public Dictionary<Vector2Int, Collection> cardLocations.

not

public Vector2Int cardLocations;

Из-за вышеприведенного утверждения ваша override bool CanConvert(Type objectType) функция должна проверять typeof(Dictionary<Vector2Int, Collection>) не typeof(Vector2Int).Кроме того, вам также необходимо добавить проверки, чтобы определить, когда запускать пользовательский десериализатор.

Сериализация :

1 . В *Функция 1027 *, которая сериализует json, вам нужно только настроить часть Dictionary<Vector2Int, Collection>, которая вызывает исключение, поэтому убедитесь, что пользовательский код запускает only , когда тип Dictionary<Vector2Int, Collection>.Вы можете сделать это, поместив код внутри if (value is Dictionary<Vector2Int, Collection>).

2 . Получите словарь для сериализации из второго аргумента.Вызовите writer.WriteStartArray(), чтобы вы записали каждое значение в виде массива.Теперь выполните цикл по Dictionary, запишите ключ с помощью writer.WriteValue(key), затем запишите значение с помощью serializer.Serialize(writer, entry.Value);.

3 .После / вне вызова цикла writer.WriteStartArray(); затем вернитесь из этой функции.

Если if (value is Dictionary<Vector2Int, Collection>) из # 1 равно false, просто позвоните writer.WriteStartObject(), а затем writer.WriteEndObject().


De-Сериализация :

4 . Определить, когда запускать пользовательский десериализатор

Мы использовали if (value is Dictionary<Vector2Int, Collection>) в # 1 при сериализациичтобы определить, должен ли пользовательский код сериализатора выполняться, но для десериализации, мы используем if (reader.TokenType == JsonToken.StartArray), чтобы определить, достигли ли мы части массива, где мы выполнили нашу пользовательскую сериализацию в функции ReadJson.

5 . Используйте JArray.Load(reader); для получения данных массива, которые мы сериализовали.В возвращенном массиве первый элемент в нем является ключом в Dictionary. Второй элемент - это значение в Dictionary.Третий элемент - это второй ключ в Dictionary.Четвертый элемент - это второе значение в Dictionary и т. Д.

6 . Разделяйте ключи и значения в JArray.

.Зацикливайте шаг JArray на 2 вместо 1.Увеличивая на 2, ключ можно легко получить с помощью JArray[loop + 0], а значение можно получить в том же цикле с помощью 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 .Получите ключ в формате Vector2Int.

Просто десериализуйте ключ из # 6 в Vector2Int с помощью JsonConvert.DeserializeObject<Vector2Int>(key).

8.. Получить значение в формате Collection.

Просто десериализовать значение из # 6 в Collection с помощью JsonConvert.DeserializeObject<Collection>(value).

9 . Восстановите данные

Создайте новый экземпляр Dictionary<Vector2Int, Collection>, затем добавьте в него как ключ из # 7 , так и значение из # 8 и затем верните его из функции ReadJson.

Если if (reader.TokenType == JsonToken.StartArray) из # 4 равно false, просто создайте и верните новый экземпляр Dictionary<Vector2Int, Collection> из функции ReadJson без добавления ключа или значения.

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();
    }
}

Использование :

Сериализация

string json = JsonConvert.SerializeObject(userInfo, new Vec2DictionaryConverter());

Десериализация

UserInfo obj = JsonConvert.DeserializeObject<UserInfo>(json, new Vec2DictionaryConverter());
0 голосов
/ 26 мая 2018

Вручную проанализируйте строку и создайте нужный тип.

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
    var vector2IntString = reader.Value.ToString();//expecting "(x, y)"
    Debug.Log(vector2IntString);
    var parts = vector2IntString.Split(new char[]{ '(', ')', ',', ' '}, StringSplitOptions.RemoveEmptyEntries);
    var x = int.Parse(parts[0]);
    var y = int.Parse(parts[1]); 
    var vector2Int = new Vector2Int(x, y);
    return vector2Int;
}

Вышеприведенное берет строку "(x, y)" и извлекает значения x и y.Он преобразует их в целые числа и использует их для инициализации экземпляра нужного типа (Vector2Int)

. Некоторые дополнительные сведения об этом конкретном сценарии показали

Если вам нужен только типпреобразование для JSON, вы также можете использовать собственные преобразователи типов JSON.NET.Хотя это не работает в случае ключей словаря.

Наконец, если в качестве ключа словаря у вас есть сложный тип, вы можете подумать о том, чтобы просто сериализовать его как коллекцию и затем десериализовать его с помощью JSON.Конвертер .NET.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...