我正在开发一个统一项目,您可以在其中保存具有自定义类型 GraphNode 的键的字典,并且每个键的值都是相同类型 GraphNode 的列表
Dictionary<GraphNode, List<GraphNode>>
。我正在使用 NewtonSoft 库将 json 从对象转换为 json 文本以及从文本转换为对象。
游戏数据脚本:
using System;
using System.Collections.Generic;
[Serializable]
public class GameData
{
public Dictionary<GraphNode, List<GraphNode>> allGraphNodes;
public GameData()
{
allGraphNodes = new Dictionary<GraphNode, List<GraphNode>>();
}
}
GraphNode 类:
public class GraphNode
{
public enum Type
{
entrance = 0,
enemy = 1,
hub = 2,
shop = 3,
boss = 4
}
public Type type;
public GraphNode(Type type2)
{
SetType(type2);
}
public void SetType(Type type2)
{
type = type2;
}
}
我在stackoverflow上尝试了类似的例子,但它不起作用,这是为了保存:
public void Save(GameData data)
{
string text = Path.Combine(dataDirPath, dataFileName);
try
{
Directory.CreateDirectory(Path.GetDirectoryName(text));
string text3 = JsonConvert.SerializeObject(data, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
});
if (useEncryption)
{
text3 = EncryptDecrypt(text3);
}
using FileStream stream = new FileStream(text, FileMode.Create);
using StreamWriter streamWriter = new StreamWriter(stream);
streamWriter.Write(text3);
}
catch (Exception ex)
{
Debug.LogError("Error" + text + "\n" + ex);
}
}
保存的字典示例如下所示:
{
"$type": "GameData, Assembly-CSharp",
"allGraphNodes": {
"$type": "System.Collections.Generic.Dictionary`2[[GraphNode, Assembly-CSharp],[System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib]], mscorlib",
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 0
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 3
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 2
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 3
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 2
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 2
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 4
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 2
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 4
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 3
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 2
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 3
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 2
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 2
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 3
}
]
},
"GraphNode": {
"$type": "System.Collections.Generic.List`1[[GraphNode, Assembly-CSharp]], mscorlib",
"$values": [
{
"$type": "GraphNode, Assembly-CSharp",
"type": 1
},
{
"$type": "GraphNode, Assembly-CSharp",
"type": 4
}
]
}
}
}
这用于加载(出现错误的部分被标记为重要行):
public GameData LoadLayout()
{
string text = Path.Combine(dataDirPath, dataFileName);
GameData result = null;
if (File.Exists(text))
{
try
{
string text2 = "";
using (FileStream stream = new FileStream(text, FileMode.Open))
{
using StreamReader streamReader = new StreamReader(stream);
text2 = streamReader.ReadToEnd();
Debug.Log(text2);
}
if (useEncryption)
{
text2 = EncryptDecrypt(text2);
}
result = JsonConvert.DeserializeObject<GameData>(text2, new JsonSerializerSettings //important line
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
});
return result;
}
catch (Exception ex)
{
Debug.LogError("Error" + text + "\n" + ex);
return result;
}
}
return result;
}
错误打印:
Newtonsoft.Json.JsonSerializationException:无法将字符串“GraphNode”转换为字典键类型“GraphNode”。创建一个 TypeConverter 将字符串转换为键类型对象。路径“allGraphNodes.GraphNode”,第 5 行,位置 16。 ---> Newtonsoft.Json.JsonSerializationException:将值“GraphNode”转换为类型“GraphNode”时出错。路径“allGraphNodes.GraphNode”,第 5 行,位置 16。 ---> System.ArgumentException:无法从 System.String 转换或转换为 GraphNode。
您遇到几个问题:
您的字典具有复杂的键,但是,正如文档中所述,由于 Json.NET 默认将字典序列化为 JSON 对象,因此序列化仅适用于可转换为字符串的键。
因此,您需要将
allGraphNodes
序列化为数组。 将字典序列化为(键值对)数组中显示的方法有多种,但最简单的可能是使用代理数组属性序列化字典。
List<GraphNode>
值可能引用字典中的其他键。要在序列化过程中保留这些引用,您需要在启用引用保留的情况下进行序列化。
不幸的是,引用保存不适用于参数化构造函数,因此
GraphNode
将需要一个无参数构造函数。只要标上[JsonConstructor]
就可以私密。
您正在使用
TypeNameHandling.All
进行序列化,但是正如文档中所述,这会带来安全风险。有关详细信息,请参阅 Newtonsoft Json 中的 TypeNameHandling 警告 和 由于 Json.Net TypeNameHandling auto?,外部 json 容易受到攻击?它还可能导致 JSON 文件出现不必要的膨胀。
您当前的数据模型不需要此设置,因为它不使用多态性。如果您确实认为有必要,请考虑编写自定义的序列化绑定器。
将所有这些放在一起,如果您按如下方式修改数据模型:
[Serializable]
public class GameData
{
[JsonIgnore]
public Dictionary<GraphNode, List<GraphNode>> allGraphNodes;
public GameData()
{
allGraphNodes = new Dictionary<GraphNode, List<GraphNode>>();
}
[JsonProperty("allGraphNodes")]
KeyValuePair<GraphNode, List<GraphNode>> [] allGraphNodeArray
{
get { return allGraphNodes?.ToArray(); }
set
{
allGraphNodes = allGraphNodes ?? new Dictionary<GraphNode, List<GraphNode>>();
if (value != null)
foreach (var pair in value)
allGraphNodes.Add(pair.Key, pair.Value);
}
}
}
[Serializable]
[JsonObject(IsReference = true)] // Forces all instances to be serialized with reference preservation
public class GraphNode
{
public enum Type
{
entrance = 0,
enemy = 1,
hub = 2,
shop = 3,
boss = 4
}
public Type type;
[JsonConstructor] // Constructor for serialization.
GraphNode() { }
public GraphNode(Type type) { SetType(type); }
public void SetType(Type type) { this.type = type; }
}
您将能够按如下方式进行序列化:
var settings = new JsonSerializerSettings
{
// Add any settings as required, e.g.
// Converters = { new StringEnumConverter() },
};
var json1 = JsonConvert.SerializeObject(gameData1, settings);
var gameData2 = JsonConvert.DeserializeObject<GameData>(json1, settings);
演示小提琴在这里。