0

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.

Klaus Gütter
  • 11,151
  • 6
  • 31
  • 36
  • I realize the string guid is at the start of the file, but in JSON its at the end? But thats how it saves the ShipsData class to Json using JsonConvert.SerializeObject(shipsData); –  Dec 23 '22 at 14:35
  • 1
    it's hard to tell the exact error from the exception information, but I'm guessing it might be because `ShipWrapperData` does not have a parameterless constructor. Try defining `public ShipWrapperData() {}` and see if that makes a difference – KMoussa Dec 23 '22 at 14:40
  • That actually fixed it, adding empty constructors to all Data scripts also added it to CargoData and ShipData for good measure. Would be really interested to now why though? I guess Json.NET uses the empty constructors internally? Too bad we get no heads up, couldve saved me half a day –  Dec 23 '22 at 14:43

1 Answers1

0

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

KMoussa
  • 1,568
  • 7
  • 11