如何反序列化带有Dictionary属性的[System.Serializable]结构体?
问题描述
我有一个标记了[System.Serializable]的结构体,初始代码如下:
[Serializable] public struct User { public string name; }
我想给它添加一个Dictionary<string, string>类型的experiments属性——因为这个属性的内容是后端动态生成的,而且键包含-,没法定义成另一个可序列化结构体。
但添加之后,experiments在反序列化时始终是null,我试过换成IDictionary类型也没用。修改后的结构体:
[Serializable] public struct User { public string name; public Dictionary<string, string> experiments; }
服务器返回的JSON格式确认如下:
{ "name": "Bob", "experiments": { "experiment-3532": "control" } }
我使用这个结构体的代码:
public IEnumerator GetUser(Action<User> callback, Action<string> errorCallback) { string url = "https://myurl.com/myurl/"; // 模拟接口地址 UnityWebRequest request = UnityWebRequest.Get(url); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("Version", Application.version); yield return request.SendWebRequest(); if (request.error == null) callback(JsonUtility.FromJson<User>(request.downloadHandler.text)); else errorCallback("Uh oh! Network error"); request.Dispose(); }
解决方案
Unity自带的JsonUtility不支持直接序列化/反序列化Dictionary类型,以下是几种可行的解决办法:
方案1:自定义可序列化字典类
实现一个继承自Dictionary并支持Unity序列化的类,借助ISerializationCallbackReceiver接口完成字典和List的双向转换:
[Serializable] public class SerializableStringDictionary : Dictionary<string, string>, ISerializationCallbackReceiver { [SerializeField] private List<string> keys = new List<string>(); [SerializeField] private List<string> values = new List<string>(); // 序列化前把字典内容转存到List public void OnBeforeSerialize() { keys.Clear(); values.Clear(); foreach (var pair in this) { keys.Add(pair.Key); values.Add(pair.Value); } } // 反序列化后从List恢复字典内容 public void OnAfterDeserialize() { this.Clear(); if (keys.Count != values.Count) throw new System.Exception($"字典键值数量不匹配:键数量{keys.Count},值数量{values.Count}"); for (int i = 0; i < keys.Count; i++) { this[keys[i]] = values[i]; } } }
然后修改User结构体的属性类型:
[Serializable] public struct User { public string name; public SerializableStringDictionary experiments; }
方案2:替换为第三方JSON库
直接使用支持Dictionary的第三方JSON库,比如Newtonsoft.Json(Json.NET),不需要修改原结构体定义,直接替换反序列化代码即可:
// 需先通过Unity Package Manager安装Newtonsoft.Json包 if (request.error == null) { User user = Newtonsoft.Json.JsonConvert.DeserializeObject<User>(request.downloadHandler.text); callback(user); }
方案3:手动解析JSON
如果不想引入第三方库,可以用轻量级JSON解析工具(比如SimpleJSON)手动提取数据:
// 需导入SimpleJSON库 if (request.error == null) { var json = JSON.Parse(request.downloadHandler.text); User user = new User(); user.name = json["name"].Value; user.experiments = new Dictionary<string, string>(); foreach (var kvp in json["experiments"].AsObject) { user.experiments.Add(kvp.Key, kvp.Value.Value); } callback(user); }
内容的提问来源于stack exchange,提问作者kbunarjo
相关产品推荐
相关产品推荐

