FormatException with Nullable int

Discussion and help for Easy Save 3
Post Reply
User avatar
Cranktrain
Posts: 16
Joined: Thu Nov 09, 2017 7:15 pm

FormatException with Nullable int

Post by Cranktrain »

Hi, I've encountered this error that sounds quite similar to the one reported in this thread. I'm using the Scripting Runtime Version '.NET 4.x Equivalent', with Mono and .NET 4.x.

I have a nullable int that I'm trying to save/load. Full stack trace on Load:

Code: Select all

FormatException: Expected '{' or "null", found '6'.
ES3Internal.ES3JSONReader.ReadNullOrCharIgnoreWhitespace (System.Char expectedChar) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3JSONReader.cs:390)
ES3Internal.ES3JSONReader.StartReadObject () (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3JSONReader.cs:108)
ES3Reader.ReadObject[T] (ES3Types.ES3Type type) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:212)
ES3Reader.Read[T] (ES3Types.ES3Type type) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:245)
ES3Reader.Read[T] () (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:98)
ES3Types.ES3Type_WorldManager.ReadComponent[T] (ES3Reader reader, System.Object obj) (at Assets/Easy Save 3/Types/ES3Type_WorldManager.cs:37)
ES3Types.ES3ComponentType.ReadObject[T] (ES3Reader reader) (at Assets/Plugins/Easy Save 3/Scripts/Types/ES3ComponentType.cs:92)
ES3Types.ES3ObjectType.Read[T] (ES3Reader reader) (at Assets/Plugins/Easy Save 3/Scripts/Types/ES3ObjectType.cs:40)
ES3Reader.ReadObject[T] (ES3Types.ES3Type type) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:215)
ES3Reader.Read[T] (ES3Types.ES3Type type) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:245)
ES3Reader.Read[T] (System.String key) (at Assets/Plugins/Easy Save 3/Scripts/Readers/ES3Reader.cs:162)
ES3.Load[T] (System.String key, ES3Settings settings) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:266)
ES3.Load[T] (System.String key, System.String filePath) (at Assets/Plugins/Easy Save 3/Scripts/ES3.cs:245)
WorldManager.CreateOrLoad (System.String saveName) (at Assets/Scripts/World/WorldManager.cs:168)
SaveAndLoadManager.SetupScene () (at Assets/Scripts/Game/SaveAndLoadManager.cs:64)
SaveAndLoadManager.OnSceneFinishedLoading (UnityEngine.SceneManagement.Scene scene, UnityEngine.SceneManagement.LoadSceneMode mode) (at Assets/Scripts/Game/SaveAndLoadManager.cs:80)
UnityEngine.SceneManagement.SceneManager.Internal_SceneLoaded (UnityEngine.SceneManagement.Scene scene, UnityEngine.SceneManagement.LoadSceneMode mode) (at C:/buildslave/unity/build/artifacts/generated/bindings_old/common/Core/SceneManagerBindings.gen.cs:245)
And here's the ES3Type that's generated, if that's of interest:

Code: Select all

using System;
using UnityEngine;

namespace ES3Types
{
	[ES3PropertiesAttribute("saveName", "Seed", "currentTreeWinterState")]
	public class ES3Type_WorldManager : ES3ComponentType
	{
		public static ES3Type Instance = null;

		public ES3Type_WorldManager() : base(typeof(WorldManager))
		{
			Instance = this;
		}

		protected override void WriteComponent(object obj, ES3Writer writer)
		{
			var instance = (WorldManager)obj;
			
			writer.WritePrivateField("saveName", instance);
			writer.WriteProperty("Seed", instance.Seed);
			writer.WritePrivateField("currentTreeWinterState", instance);
		}

		protected override void ReadComponent<T>(ES3Reader reader, object obj)
		{
			var instance = (WorldManager)obj;
			foreach(string propertyName in reader.Properties)
			{
				switch(propertyName)
				{
					
					case "saveName":
					reader.SetPrivateField("saveName", reader.Read<System.String>(), instance);
					break;
					case "Seed":
						instance.Seed = reader.Read<System.Nullable<System.Int32>>();
						break;
					case "currentTreeWinterState":
					reader.SetPrivateField("currentTreeWinterState", reader.Read<WorldManager.TreeWinterStates>(), instance);
					break;
					default:
						reader.Skip();
						break;
				}
			}
		}
	}

	public class ES3Type_WorldManagerArray : ES3ArrayType
	{
		public static ES3Type Instance;

		public ES3Type_WorldManagerArray() : base(typeof(WorldManager[]), ES3Type_WorldManager.Instance)
		{
			Instance = this;
		}
	}
}
What's going on here?

Edit: Here's the part of the save file for WorldManager, "Seed" is the nullable int here:
"WorldManager":{"__type":"WorldManager,Assembly-CSharp","value":{"_ES3Ref":8971381811690773543,"goID":469276610457707396,"saveName":"RandomZoo\/newsaveformat","Seed":670424350,"currentTreeWinterState":0}}
Edit 2: Oh, is it just that Easy Save 3 doesn't support nullable types? I would have been expecting an ES3 Type Error on Save, in that case.
User avatar
Joel
Moodkie Staff
Posts: 4849
Joined: Wed Nov 07, 2012 10:32 pm

Re: FormatException with Nullable int

Post by Joel »

Hi there,

As mentioned in the Supported Types page, generic types are not automatically supported. I'll look into why it's letting you add the generic type when creating the ES3Type and ensure that it's fixed in the next update.

There's a Feature Request for Nullable types here: https://moodkie.com/forum/viewtopic.php?f=14&t=1339

In the meantime, the easiest solution is to create a wrapper for your nullable type which removes the generic parameter and modify the ES3Type to write and read this. The wrapper would look like this:
public class NullableIntWrapper
{
	public int intVal;
	public bool isNull;

	// Parameterless constructor so it's automatically serialisable.
	public NullableIntWrapper(){}

	public NullableIntWrapper(System.Nullable<int> nullable)
	{
		isNull = (nullable == null);
		if(!isNull)
			intVal = nullable.Value;
	}

	public System.Nullable<int> Get()
	{
		if(isNull)
			return null;
		return new System.Nullable<int>(intVal);
	}
}
And then in the ES3Type's Write method, instead of:
writer.WriteProperty("Seed", instance.Seed);
You would use:
writer.WriteProperty("Seed", new NullableIntWrapper(instance.Seed));
And in the Read method, instead of:
case "Seed":
    instance.Seed = reader.Read<System.Nullable<System.Int32>>();
    break;
You would do:
case "Seed":
    instance.Seed = reader.Read<NullableIntWrapper>().Get();
    break;
All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
User avatar
Cranktrain
Posts: 16
Joined: Thu Nov 09, 2017 7:15 pm

Re: FormatException with Nullable int

Post by Cranktrain »

Thanks Joel, much appreciated.
Post Reply