JSON Serializing Immutable Variables

So I have a messy problem…

I want to use JSON to serialize between JSON files and some nested classes I have. The problem I’m having is it’s difficult to set them up without making the variables public and editable by external sources.

To make a variable/class accessible by the JSON Serializser (either JSONUtility or System.Web.Script.Serialization) the variable must be public. The issue is I don’t want to make these variables changeable at runtime by an external programmer (they are in a managed dll module).

I can’t use public properties or internal variables as the JSON serializer won’t accept them. So I’m unsure how I can avoid making them public and editable . This is what they look like:

#1
May 3rd 2016, 1:44:35 pm

VALID JSON (RFC 4627)
Formatted JSON Data
{
   "Contents":[
      {
         "name":"Chapter 1",
         "description":"First Chapter Description",
         "isComplete":false,
         "percentageComplete":0,
         "sections":[
            {
               "tests":[
                  {
                     "id":"TEST_001_001_0001",
                     "name":"First Test Description",
                     "description":"Do Test - chapter 1 section 1 test 1",
                     "isComplete":false
                  },
using System.Web.Script.Serialization;

namespace Learning
{
    /// <summary>
    /// These are equal to one single point. The sum of these equal the completion of a section, and subsequent chapter.
    /// </summary>
    public class Test
    {
        public Test(){ } // Standard constructor kepy as JSON Serializer cannot handle constructor parameters
        public Test(string id, string name, string description)
        {
            this.name = name;
            this.id = id;
            this.description = description;
        }

        public string id;
        public string name;
        public string description;
        public bool isComplete { get; protected set; }

        /// <summary>
        /// Modify complete property externally
        /// </summary>
        /// <param name="isComplete"></param>
        public void SetComplete(bool isComplete)
        {
            this.isComplete = isComplete;
        }
    }
}

unity has a json now

and if you want it to be public but not show in editor use

[HideInInspector]
public string Id;

or if you want it to be private but show in editor use

[SerializeField]
private string Id;

Hi rakkage,

Thanks for responding. I’m not meaning visible in the Inspector to designers. I mean visible to anything outside of it’s own dll module. Including any Unity Scripts. So for example the following would be considered immutable to an external program:

public string id { get; private set; }
public const string id = "someID";
public readonly string id;
internal string id;

However I cannot force JSONUtility or .NET’s serializer to find anything other than a bog standard public variable.

My module doesn’t even contain the UnityEngine/UnityEditor dll’s anymore, as I want to have my modules cross engine.

        public void RetrieveStudentInfo(string _path, System.Action<bool, Syllabus> result)
        {
            Syllabus _syllabus;
            string json = File.ReadAllText((_path + "/" + LOCALSAVEFILENAME));
            var serializer = new JavaScriptSerializer();

            if(json.Length > 1)
            {
                _syllabus = serializer.Deserialize<Syllabus>(json);
                result(true, _syllabus);
            }
            else
            {
                result(false, null);
            }
        }

        public bool SaveStudentData(Syllabus _syllabus, string _path)
        {
            var serializer = new JavaScriptSerializer();
            string json = serializer.Serialize(_syllabus);

            if(json.Length > 0)
            {
                File.WriteAllText(_path + "/" + LOCALSAVEFILENAME, json);
                return true;
            }
            else
            {
                return false;
            }
           
        }

Can’t the built-in JsonUtility class serialize private fields with the [SerializeField] attribute?

That could do the job for you, but then you’re dependent on UnityEngine again. If that’s not an option, you’ll have to be clever.

1 Like

Consider using something like Memento pattern. For your business logic class with properly encapsulated data, create corresponding POCO DTO for serialization purposes only. Add methods like GetMemento() and CreateFromMemento(). So MyActualClass is properly encapsulated with no public fields; and MyClassMemento contains no logic, just public simple fields for serialization.

If you’re using the .Net JSON serializer found as JavaScriptSerializer in System.Web.Script.Serialization:

There’s various limitations of it, which include that it only serializes public writable fields/properties.

Which it describes in a very cryptic manner on the doc page:

I’ve no idea why the heck they didn’t include any attributes for it to control serializability… but they didn’t.

Try using the DataContract model if you want to avoid the Unity json serializer:

For more robustness, you can also check out 3rd party serializers… like Newtonsoft JSON.Net:
Json.NET - Newtonsoft

(it supports all versions of .Net back to 2.0)

You may notice that the documentation for the JavaScriptSerializer you’re using actually says at the top of its page:

I swear, that doc page is awful! It must be a one-off class that was put in for the web api, and then obsoleted in favor of Json.NET, and only kept in there for backwards compatibility.

It’s better to split logic classes from DTO regardless of actual serialization technology used: MiniJSON, UnityEngine.JsonUtility, Newtonsoft Json.NET, etc.

Just to add to this, my Unity port of JSON .NET is getting ready for an update. I’ve been migrating it to JSON .NET 8.0.4 (the latest and greatest) in which case it will now support CustomCreationConverter in addition to JsonConverter which will allow you to use an alternate constructor.

there is also a free & open source version of json.net for unity

1 Like