带有Vector2Int类型的密钥的JsonConvert字典

时间:2018-05-26 01:20:14

标签: c# unity3d serialization json.net

我正在开发一款名为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更改为LineupDictionary<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;
}

3 个答案:

答案 0 :(得分:1)

源自JsonConverter的自定义序列化程序需要更多工作且错误很少。首先,请注意您尝试序列化/反序列化Dictionary Vector2Int Vector2Int而不仅仅是public Dictionary<Vector2Int, Collection> cardLocations. 变量。

就是这样:

public Vector2Int cardLocations;

override bool CanConvert(Type objectType)

由于上述声明,您的typeof(Dictionary<Vector2Int, Collection>)函数应该检查typeof(Vector2Int) 不是 WriteJson。此外,您还需要添加检查以确定何时运行自定义反序列化器。

<强>序列化

1 。在序列化json的Dictionary<Vector2Int, Collection>函数中,您只需要自定义引发异常的Dictionary<Vector2Int, Collection>部分,以确保自定义代码运行类型为if (value is Dictionary<Vector2Int, Collection>)。您可以将代码放在writer.WriteStartArray()

2 。从第二个参数中获取字典序列化。调用Dictionary,以便将每个值都写为数组。现在,循环遍历writer.WriteValue(key),使用serializer.Serialize(writer, entry.Value);写入密钥,然后使用writer.WriteStartArray();写入值。

第3 即可。循环之后/之外调用if (value is Dictionary<Vector2Int, Collection>)然后从该函数返回。

如果#1 中的falsewriter.WriteStartObject(),则只需拨打writer.WriteEndObject(),然后拨打if (value is Dictionary<Vector2Int, Collection>)

<强>解串行化

4 。确定何时运行自定义反序列化器

我们在序列化时使用#1 中的if (reader.TokenType == JsonToken.StartArray)来确定自定义序列化代码是否应该运行但是为了反序列化,我们使用ReadJson来确定我们是否已达到我们在JArray.Load(reader);函数中进行自定义序列化的数组部分。

5 。使用Dictionary获取我们序列化的数组数据。在返回的数组中,其中的第一个元素是Dictionary中的键。第二个元素是Dictionary中的值。第三个元素是Dictionary中的第二个键。第四个元素是JArray ans中的第二个值。

6 。分隔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(); } 在同一循环中检索该值。

Vector2Int

7 。以Vector2Int格式获取密钥。

只需使用JsonConvert.DeserializeObject<Vector2Int>(key)将密钥从#6 反序列化为Collection

8 。以Collection格式获取值。

只需使用JsonConvert.DeserializeObject<Collection>(value)#6 中的值反序列化为Dictionary<Vector2Int, Collection>

9 。重新构建数据

创建ReadJson的新实例,然后将#7 中的密钥和#8 中的值添加到其中,然后从{{1}返回功能。

如果#4 中的if (reader.TokenType == JsonToken.StartArray)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());

答案 1 :(得分:0)

手动解析字符串并创建所需类型。

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自定义转换器对其进行反序列化。

答案 2 :(得分:0)

对于像我这样的人也会遇到同样的问题。

@Programmer的答案是正确的,但可能已过时(我不知道Json.NET在2018年的样子),而且代码太多(至少对我而言)。

这是我的解决方法:

def example(data):
    data.vx=<conversion>
    data.vth=<conversion>

def listener():
    rospy.Subscriber('*topic*', Odometry, example)
    rospy.spin()

if __name__ == 'main':
    listener()
  1. using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; ... [JsonConverter(typeof(Vec2DictionaryConverter))] public Dictionary<Vector2Int, Collection> cardLocations; ... public class DictionaryConverter : JsonConverter<Dictionary<Vector2Int, Collection>> { private static readonly string[] SEPARATORS = new[] { "(", ", ", ")" }; public override Dictionary<Vector2Int, Value> ReadJson(JsonReader reader, Type objectType, Dictionary<Vector2Int, Collection> existingValue, bool hasExistingValue, JsonSerializer serializer) { if (JsonToken.Null == reader.TokenType) { return null; } var dictionary = new Dictionary<Vector2Int, Collection>(); foreach (var pair in JObject.Load(reader)) { var vector = pair.Key.Split(SEPARATORS, StringSplitOptions.RemoveEmptyEntries) .Select(it => Convert.ToInt32(it)); var key = new Vector2Int(vector.First(), vector.Last()); var value = pair.Value.ToObject<Collection>(serializer); dictionary.Add(key, value); } return dictionary; } public override void WriteJson(JsonWriter writer, Dictionary<Vector2Int, Collection> value, JsonSerializer serializer) { serializer.Serialize(writer, value); } } 属性被使用,而不是始终将Converter传递给JsonConverter
  2. 使用了通用SerializeObject/DeserializeObject,因此不再需要实现JsonConverter<Dictionary<Vector2Int, Collection>>
  3. 由于OP在序列化方面没有问题,因此仅实现了CanConvert

尽管我们依赖于ReadJson的实现,但这种方法很好用,但我发现Unity的Vector2Int.ToString的序列化至少在我的测试中更快。