You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

调用链

  1. GameControl层调用:
public void ContinueLoading()
{
    ShipsData shipsData = SaveManager.instance.LoadShips(currentSaveSlot);
}
  1. SaveManager层转发:
public ShipsData LoadShips(int saveSlot)
{
    print("slotindex: " + saveSlot);

    return (ShipsData)PSD.Load<ShipsData>(slotPath + saveSlot.ToString() +
                                          shipsDataFileName);
}
  1. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 05:05:38