如何加载具有自定义类型和自定义类型列表的字典

问题描述 投票:0回答:1

我正在开发一个统一项目,您可以在其中保存具有自定义类型 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。

dictionary unity-game-engine json.net type-conversion
1个回答
0
投票

您遇到几个问题:

  1. 您的字典具有复杂的键,但是,正如文档中所述,由于 Json.NET 默认将字典序列化为 JSON 对象,因此序列化仅适用于可转换为字符串的键。

    因此,您需要将

    allGraphNodes
    序列化为数组。 将字典序列化为(键值对)数组中显示的方法有多种,但最简单的可能是使用代理数组属性序列化字典。

  2. 字典中的
  3. List<GraphNode>
    值可能引用字典中的其他键。要在序列化过程中保留这些引用,您需要在启用引用保留的情况下进行序列化。

  4. 不幸的是,引用保存不适用于参数化构造函数,因此

    GraphNode
    将需要一个无参数构造函数。只要标上
    [JsonConstructor]
    就可以私密。

  5. 您正在使用

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

演示小提琴在这里

© www.soinside.com 2019 - 2024. All rights reserved.