C# Json.NET:JsonConvert.DeserializeObject解析ShipsData类失败排查
排查ShipsData类Json反序列化失败问题
问题概述
使用Newtonsoft.Json进行数据序列化/反序列化时,仅ShipsData类加载时抛出NullReference异常,其他类均正常工作;保存ShipsData能生成格式正确的JSON文件,路径与文件名无误,调试显示读取到的JSON字符串完全正确,但反序列化环节触发异常。
相关代码
加载逻辑(FileDataHandler)
public System.Object Load<T>() { try { string dataAsJson = ""; using (FileStream stream = new FileStream(fullPathAndFileName, FileMode.Open)) { using (StreamReader reader = new StreamReader(stream)) { dataAsJson = reader.ReadToEnd(); Debug.Log("Loaded data as JSON: " + dataAsJson); // 调试显示正确JSON Debug.Log(JsonConvert.True); // 调试显示为true T obj = JsonConvert.DeserializeObject<T>(dataAsJson); // 疑似失败点 return obj; } } } catch (Exception e) { Debug.LogError("failed to load: " + e.Message); // 异常消息为NullReference return false; } }
数据类定义
[System.Serializable] public class ShipsData { public List<ShipWrapperData> ShipWrappers; public ShipsData() { ShipWrappers = new List<ShipWrapperData>(); } } [System.Serializable] public class ShipWrapperData { public string guid { get; private set; } = "a unique guid"; public bool IsPlayer; public bool IsPlayerFleet = true; public CargoData cargoData; public ShipData shipData; public ShipWrapperData(Sailing.ShipWrapper shipWrapper) { this.guid = shipWrapper.guid; this.IsPlayer = shipWrapper.IsPlayer; this.IsPlayerFleet = shipWrapper.IsPlayerFleet; this.cargoData = new CargoData(shipWrapper.cargo); this.shipData = new ShipData(shipWrapper.ship); } }
调用链
- GameControl层调用:
public void ContinueLoading() { ShipsData shipsData = SaveManager.instance.LoadShips(currentSaveSlot); }
- SaveManager层转发:
public ShipsData LoadShips(int saveSlot) { print("slotindex: " + saveSlot); return (ShipsData)PSD.Load<ShipsData>(slotPath + saveSlot.ToString() + shipsDataFileName); }
- PersistentDataManager层初始化处理器:
public System.Object Load<T>(string fullPathAndFileName) { FileDataHandler dataHandler = new FileDataHandler(fullPathAndFileName); return dataHandler.Load<T>(); }
保存的JSON文件内容
{ "ShipWrappers": [ { "IsPlayer": false, "IsPlayerFleet": false, "cargoData": { "currency": 0, "cargoItems": [], "gunItems": [], "equippedGunItems": [], "shotItems": [] }, "shipData": { "ID": 0, "xPosition": 295.6506, "zPosition": 1259.3418, "Heading": 359.999, "NationID": 0, "Name": "", "Price": 0, "health": 900, "healthMax": 900, "sailHealth": 619, "sailHealthMax": 619, "speedMax": 11.5916224, "cargo": 909, "cargoMax": 909, "crew": 77, "crewMax": 77, "crewMorale": 63, "gunCollectionID": 0, "boatTypeID": 0, "Boats": 2, "BoatsMax": 2, "armor": 43, "fouling": 75 }, "guid": "18d5ac36-404a-4360-a558-2feb90727159" } ] }
问题根源与解决方案
1. 核心问题:guid属性的私有setter
ShipWrapperData类中guid属性使用了private set:
public string guid { get; private set; } = "a unique guid";
Newtonsoft.Json默认无法对私有setter的属性赋值,反序列化时尝试创建ShipWrapperData实例,但无法为guid赋值,结合无参构造函数缺失的问题,最终触发NullReference异常。
2. 辅助问题:ShipWrapperData缺少无参构造函数
ShipWrapperData仅定义了带参数的构造函数,Newtonsoft.Json反序列化时优先尝试无参构造函数创建实例,缺少该构造函数会加剧初始化失败概率。
解决步骤
方法一:修改guid的setter为公共
允许Json.NET直接赋值:
public string guid { get; set; } = "a unique guid";
方法二:保留私有setter,添加Json特性
通过[JsonProperty]授权Json.NET访问私有setter:
[JsonProperty] public string guid { get; private set; } = "a unique guid";
方法三:添加无参构造函数
给ShipWrapperData添加无参构造函数(即使是private,Json.NET也能访问):
private ShipWrapperData() { }
额外验证与优化
- 检查CargoData和ShipData类是否存在相同的私有setter或无参构造函数缺失问题,确保嵌套类反序列化正常。
- 捕获完整异常信息,将catch块中的
e.Message改为e.ToString(),可以查看完整堆栈定位问题:
catch (Exception e) { Debug.LogError("failed to load: " + e.ToString()); return null; // 原返回false不合适,建议返回null匹配Object类型 }
内容的提问来源于stack exchange,提问作者user20494201
相关产品推荐
相关产品推荐

