Spice Road Development

JSON Serialization in Unity

JSONUtility (Unity Class)

Unity has it’s own JSON serialization package [ref] which is very close in usage to the native C# serialization. It works with public field, or private fields marked with the ‘SerializeField’ attribute. It does not work with class properties, or fields marked ‘readonly’. To otherwise skip a field mark it with the ‘NonSerialized’ attribute.

// Will serialize
public string name;
[SerializeField] private string value;

// Will not serialize
public readonly string name;
public string name {get; set;}
[NonSerialized] int age;

The serialization method does not require type information, it will simply create json text based on the public field values. This means you do not need to deserialize into the same object you serialized from. The class it’s self must be marked with the ‘Serializable’ attribute.

using UnityEngine;

[System.Serializable]
public class PlayerState : MonoBehaviour{
    public string name;
    public int age;

    public string SaveToString(){
        return JsonUtility.ToJson(this);
    }

    // Given:
    // name = "Dr Charles"
    // age = 33
    // SaveToString returns:
    // {"name":"Dr Charles","age":33}
}

Unlike serialization, deserialization requires a type. Any values found in the JSON text that are not fields in the provided type will be ignored.

PlayerState playerState = JsonUtility.FromJson<PlayerState>(string);

JSONEncoder (Helper Class)

In order to automatically decode a json object to the correct type I have included the type name in the JSON text object. The emitted JSON has two root fields: type & instance.

using UnityEngine;
using System;

public class JSONDetails<T>{
    public T instance;
    public string type;

    public JSONDetails(T instance) {
        this.instance = instance;
        this.type = instance.GetType().ToString();
    }    
}

public class JSONEncoder {
    public string type = "";
    public JSONEncoder() { }

    public static string Serialize<T>(T anObject) {
        return JsonUtility.ToJson(new JSONDetails<T>(anObject), true);
    }

    public static object Deserialize(string json) {
        JSONEncoder wrapper = JsonUtility.FromJson<JSONEncoder>(json);
        Type g = Type.GetType(wrapper.type);
        Type t = typeof(JSONDetails<>).MakeGenericType(new Type[] { g });
        var hydrated = JsonUtility.FromJson(json, t);   
        return t.GetField("instance").GetValue(hydrated);
    }
}

At times serialization may require type information to ensure the correct type is serialized. Deserialization returns an object which can be cast as necessary. Typically this should used to detect the presence of an interface and cast accordingly.

public void ReceiveEvent(string json, string source) {
    var isEvent = JSONEncoder.Deserialize(json);

    if (isEvent is IModelEvent<M> != false) {
        IModelEvent<M> modelEvent = (IModelEvent<M>)isEvent;
        this.model = modelEvent.UpdateModel(this.model);
    }

    if (isEvent is IModelEvent<N> != false) {
        this.Broadcast(isEvent, source);
    }
}