我正在开发一款名为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;
}
答案 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 中的false
为writer.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()
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
SerializeObject/DeserializeObject
,因此不再需要实现JsonConverter<Dictionary<Vector2Int, Collection>>
CanConvert
尽管我们依赖于ReadJson
的实现,但这种方法很好用,但我发现Unity的Vector2Int.ToString
的序列化至少在我的测试中更快。