Home > Software design >  Json.NET C# - Why is JsonConvert.DeserializeObject failing when using class A but not class B? Every
Json.NET C# - Why is JsonConvert.DeserializeObject failing when using class A but not class B? Every

Time:12-24

I cant figure this out:

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); // This debugs as the correct json string.
                Debug.Log(JsonConvert.True); // This debugs as true;

                T obj = JsonConvert.DeserializeObject<T>(dataAsJson); // This is where it fails?

                return obj; // Or maybe here?
            }
        }

    }
    catch (Exception e)
    {
        Debug.LogError("failed to load: "   e.Message); // Message == NullReference
        return false;
    }
}

It only happens with ShipsData class, using other Classes will Load correctly. Saving the ShipData class works correctly and json is produced correctly using JsonConvert.SerializeObject.

All paths and file names are correct.

Other scripts:

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

Step 1. GameControl:

public void ContinueLoading()
{
    ShipsData shipsData = SaveManager.instance.LoadShips(currentSaveSlot);

}

Step2: SaveManager:

public ShipsData LoadShips(int saveSlot)
{
    print("slotindex: "   saveSlot);

    return (ShipsData)PSD.Load<ShipsData>(slotPath   saveSlot.ToString()  
                                          shipsDataFileName);
}

Step3. PersistentDataManager:

public System.Object Load<T>(string fullPathAndFileName)
{
    FileDataHandler dataHandler = new FileDataHandler(fullPathAndFileName);
    return dataHandler.Load<T>();
}

Step4. 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); //This debugs as the correct json string.
                Debug.Log(JsonConvert.True); // This debugs as true;

                T obj = JsonConvert.DeserializeObject<T>(dataAsJson); // This is where it fails?

                return obj; // Or maybe here?
            }
        }

    }
    catch (Exception e)
    {
        Debug.LogError("failed to load: "   e.Message); // Message == NullReference
        return false;
    }
}

Everything debugs fine, and this works with other classes. Saving also works as intended on all classes including this one ShipsData. So Im trying to figure out, what could be causing DeserializeObject() to fail?

enter image description here

The first exception from e.Message in the above DataFileHandler.cs script.

enter image description here

The second error occurs back in the original SaveManager script, and I think its only happening because of the first error, so then trying to cast it to (ShipsData) fails because JsonConvert.DeserializeObject() returns a corrupted object?

The ShipsData.json file:

{
  "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"
    }
  ]
}

I cant get the json to format right here on SO(if someone can edit this please do).

But its correct Json format, saving works correctly. Loading and Saving works correctly using the above scripts for all classes except ShipsData for some reason...

Thank you.

CodePudding user response:

Adding an answer to reflect the comments on the question:

The problem was due to some types lacking a paramterless constructor (or a constructor matching property names), which results in Json.net not being able to create instances of those types while deserializing

Worth noting that you can define a private parameterless constructor and instruct Json.net to use that by using the ConstructHandling setting as shown here https://www.newtonsoft.com/json/help/html/DeserializeConstructorHandling.htm

  • Related