SimpleJson Serializing,SimpleJSON Unity Serialization

Hi,

I’m trying to serialize/deserialize this class using SimpleJSON in unity. Here is what I have so far:

using System.Collections.Generic;
using static Game.GameFlags;
using SimpleJSON;

namespace Game
{
    [System.Serializable]
    public class GameData
    {
        public int _Coins = 0;
        public int _Level = 1;

        public GameData(int c = 0, int l = 1)
        {
            _Coins = c;
            _Level = l;
        }
    }

    [System.Serializable]
    public class PlayerData
    {
        public bool _FirstTimeUser = false;
        public int _Keys = 0;
        public int _Gems = 0;
        public GameInfo _CurrentGameInfo;
        public CharacterInfo _CurrentCharacterInfo;

        // Game Specific
        public Dictionary<string, GameData> _GameData = new Dictionary<string, GameData>();

        // Shop
        public List<CharacterInfo> _Characters = new List<CharacterInfo>();

        public JSONNode Serialize()
        {
            var n = new JSONObject();
            n["_FirstTimeUser"] = _FirstTimeUser;
            n["_Keys"] = _Keys;
            n["_Gems"] = _Gems;
            n["_CurrentGameInfo"] = _CurrentGameInfo;
            n["_CurrentCharacterInfo"] = _CurrentCharacterInfo;

            var h = n["_GameData"].AsObject;

            foreach (var v in _GameData)
                h[v.Key] = v.Value;

            n["_Characters"] = _Characters.

            return n;
        }

        public void Deserialize(JSONNode aNode)
        {
            
        }
    }
}

n[“_CurrentGameInfo”] = _CurrentGameInfo;
n[“_CurrentCharacterInfo”] = _CurrentCharacterInfo;

This throws errors as

Cannot convert GameInfo to JSONnode

and these are not able to be serialized?

// Game Specific
public Dictionary<string, GameData> _GameData = new Dictionary<string, GameData>();

    // Shop
    public List<CharacterInfo> _Characters = new List<CharacterInfo>();

Thank you

Well, of course this won’t work like that out of the box. Inside your Serialize method you try to store a GameInfo instance as JSONNode. By default only primitive types are supported. You have the same issue with your GameData classes inside your dictionary. When using this library you essentially have 3 options:

  • Since your PlayerData class already uses a method called “Serialize” you could just add similar methods to your GameInfo and GameData classes. In that case instead of assigning the GameInfo / GameData instance you would assign the result of its Serialize method instead.
  • Another option is to implement a custom implicit conversion operator inside the JSONNode class. The framework is designed in a way so you can simply add another partial class in a completely seperate file and just add those conversion operators. That would mean the C# compiler is able convert between a GameInfo / GameData object and a JSONNode automatically (well the actual conversion is done by you in those operators, but it’s kinda transparent everywhere else).
  • Your third option is to just handle this sub information yourself inside the Serialize method of your PlayerData. We don’t know how your GameData class looks like but since there (currently) isn’t that much data in your GameData class you could simply handle the two fields implicitly inside the foreach loop. It’s up to you if you want to use a complete sub object or just a sub array to store the two values. You just have to be consistent when you deserialize it again ^^.

Since we don’t know how your GameInfo class looks like I will focus on the GameData class for the examples:

So for the first solution you could modify your GameData class like this:

[System.Serializable]
public class GameData
{
    public int _Coins = 0;
    public int _Level = 1;

    public GameData(int c = 0, int l = 1)
    {
        _Coins = c;
        _Level = l;
    }
    public JSONNode Serialize()
    {
        var n = new JSONObject();
        n["_Coins"] = _Coins;
        n["_Level"] = _Coins;
        return n;
    }
}

so now you can do this in your foreach loop:

foreach (var v in _GameData)
    h[v.Key] = v.Value.Serialize();

Of course in order to deserialize you would need the opposite as well. Since you declared a parameterized constructor there will be no default constructor. However you can also create a constructor that takes a JSONNode

public GameData(JSONNode aNode)
{
    _Coins = aNode["_Coins"];
    _Level = aNode["_Level"];
}

That way you can easily recreate the instance during deserialization. A similar thing could be done for your GameInfo class.

For the second solution you just have to create another partial “class part” or the JSONNode class and add the operators which do the conversion for you.

namespace SimpleJSON
{
    public partial class JSONNode
    {
        public static implicit operator JSONNode(GameData aData)
        {
            var n = new JSONObject();
            n["_Coins"] = _Coins;
            n["_Level"] = _Coins;
            return n;
        }

        public static implicit operator GameData(JSONNode aNode)
        {
            return new GameData(aNode["_Coins"], aNode["_Level"]);
        }
}

When doing this for your GameInfo class as well, with this code in a seperate class your original code would work out of the box because the compiler now has a way to convert a GameData instance into a JSONNode using the operator we just created. Of course you could even do the same for the PlayerData class but it usually only makes sense for small helper classes that you need more often. Keep in mind that the current version of SimpleJSON has several extension files for different purposes. So there’s one for common Unity types and one for some additional .NET types. Though when you don’t need them, you don’t have to include them. The unity extension allows to directly convert vectors, colors, quaternions and a few others.

The third solution is pretty straight forward. Instead of delegating the serialization / deserialization to other places you could just do it inplace. Again, I can only show an example for your GameData class:

foreach (var v in _GameData)
{
    h[v.Key]["_Coins"] = v.Value._Coins;
    h[v.Key]["_Level"] = v.Value._Level;
}

Of course this becomes quite impractical for larger sub objects. Note that while it’s easy to support polymorphism on the serialization side when the base class provides a virtual Serialize method, deserialization is more tricky. Json doesn’t care about the exact type you may use in C#, it doesn’t even know about those types. Each concrete class of course can handle its own serialization and deserialization, but during deserialization you have to know which concrete class was originally stored in a polymorphic field / array. For this you need a way to also store some kind of information alongside each instance so the deserializing code knows which objects it has to create. Once that’s done the actual deserialization can also be done through a virtual Deserialize method like this

public virtual void Deserialize(JSONNode aNode) {}

which can be overridden in every concrete class.

Hopefully this wasn’t too much information at once ^^. SimpleJSON is a “simple” json framework. It provides maximum flexibitity but may require a bit more work than most out-of-the-box object mappers. The great thing is you wouldn’t necessarily need the concrete C# classes in some cases. For example pure configuration data could be loaded from a file and hold in memory as JSONNode. Internally a JSONObject is just an ordinary dictionary. Yes, there would be performance issues if used in a tight loop but for a lot of thing this would work just fine.

Since you can extend the JSONNode class, you could also add other custom data to the class. However if you do this should be kept to a minimal amount. For example you could add a general purpose public GameObject field to the JSONNode class. That way you could associate any gameobject with any value or object stored in a JSONNode at runtime. Of course that wouldn’t have to do anything with the actual json serialization, but it’s just a way to use the framework as a central hub. Well, after all it’s just a versatile tool and how you use it is up to you.