Limitations on what types can be directly Serialized/Deserialized as JTokens

I was trying to do my own custom serialization/deserialization of various objects and using the SpellbornHunter repo and the JSON tutorial to guide me. I caught the bit about the Vector3 conversion and how the Newtownsoft Utility can’t handle things like structs.

Then in the sample code from Spellborn Hunter I see attempts to serialize and deserialize structs and dictionaries. The code in SpellbornHunter is much shorter and cleaner than what I’ve been trying to do.

(De)Serializing an array of structs looks very easy but this looks like a contradiction of what’s in the tutorial. It’s so easy. It’s just one line of code that handles the conversion process to/from a JToken to an array of structs.

Question: What important nuance am I misunderstanding to allow for such simple code in Spellborn Hunter but more complex code in the tutorial?

Inventory.cs

        [System.Serializable]
        private struct InventorySlotRecord
        {
            public string itemID;
            public int number;
            public JToken state;
        }

        public virtual JToken CaptureState()
        {
            var slotStrings = new InventorySlotRecord[inventorySize];
            //code continues with no access to JSON utility
            return JToken.FromObject(slotStrings);


        }

        public void RestoreFromJToken(JToken state)
        {
            var slotStrings = state.ToObject<InventorySlotRecord[]>();
            //code continues with no additional use of JSON utility
        }

StatsEquipableItem.cs


       public override JToken CaptureAsJToken()
        {
            JObject state = new JObject();
            IDictionary<string, JToken> stateDict = state;
            stateDict["level"] = JToken.FromObject(GetLevel());
            stateDict["additive"] = JToken.FromObject(actualAdditiveModifiers);
            stateDict["percentage"] = JToken.FromObject(actualPercentageModifiers);
            return state;
        }

        public override void RestoreFromJToken(JToken state)
        {
            IDictionary<string, JToken> stateDict = (JObject)state;
            SetLevel(stateDict["level"].ToObject<int>());
            actualAdditiveModifiers = stateDict["additive"].ToObject<Dictionary<EStat, float>>();
            actualPercentageModifiers = stateDict["percentage"].ToObject<Dictionary<EStat, float>>();
        }

The secret ingredient is experimentation. I will state plainly that both of these examples are not Best Practice, but that was a chance I took in a personal project. A better practice would have been to create a JObject for each of the Dictionaries (since a JObject is, under the hood, a Dictionary<string, JToken>)

Newtonsoft can handle many structs, provided that every element is, indeed serializable, right up until it can’t. In these cases, it worked. In other cases, it dramatically failed.
I’ve since discovered that some of the reason for this is code pruning

One consistent thing is that classes that weren’t serializable with BinaryFormatter still generally won’t be serializable with Newtonsoft. Quaternion and Vector3 come to mind.

It was decided at the time of writing the JsonSavingSystem, that the best way to prevent issues was to always enforce Best Practices in the tutorial. While I could have made some of the code shorter by using the same shorcuts I used here, I might have left students without the needed skills when it came time to create a proper JToken when something they wanted to save is NOT serializable.

You might have noticed one other thing looking through some of the Capture/RestoreStates… When I wrote the tutorial, I was under the (apparently mistaken) impression that you had to map an IDictionary<string, JToken> over the JObject, and an IList overtop the JArrays. This turns out to be incorrect. You can access keys directly from a JObject, in the exact same way you access a Dictionary.

1 Like

It’s curious that SerializableVector3 was not. The only difference I noted between that and the struct in Spellborn Hunter is that SerializeableVector3 has methods whereas the rest are just very basic structs.

OK this makes sense. I appreciate that. I learned a lot. I know a little bit about JSON and was curious though why it wasn’t simpler than it seemed since so much of what we’re saving is representable with a Dictionary. What you did in Spellborn Hunter confirms my hunch of what I thought it would look like.

Yes! It was one of the first things I noticed. I figured maybe it was for being more explicit in a learning environment? But gosh it is hard to keep track of all the variable names when you’re trying to save something more complex. I also noticed a typo in one of the examples on the JSON wiki where you used stateDict instead of state or vice versa.

Ok thanks. That answers it.

1 Like

It may be as simple as that. Every attempt I made to use SerializableVector3 was a failure. Personally, I thik the ToVector and FromJToken static methods take care of this more cleanly than the SerializableVector3 anyways.

The Saving system tutorial is actually overdue for a rewrite, and I plan to better explain some of those things (along with removing the IDictionary/IList stuff… that’s completely unneeded complexity.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms