SkillAgentSearch skills...

Fullserializer

A robust JSON serialization framework that just works with support for all major Unity export platforms.

Install / Use

/learn @jacobdufault/Fullserializer
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Looking for maintainer

Please see #154.

Full Serializer

Full Serializer is an easy to use and robust JSON serializer that just works. It'll serialize pretty much anything you can throw at it and work on every major Unity platform, including consoles. Full Serializer doesn't use exceptions, so you can activate more stripping options on iOS.

Best of all, Full Serializer is completely free to use and available under the MIT license!

Why?

There were no serializers that just work in Unity that are free and target all export platforms. Full Inspector needed one, so here it is.

Installation

Import the Source folder into your Unity project! You're good to go! (Also see the DLL based import at the bottom of this document).

Usage

Usage is identical to Unity's default serialization, except that you don't have to mark types as [Serializable]. Here's an example:

struct SerializedStruct {
    public int Field;

    public Dictionary<string, string> DictionaryAutoProperty { get; set; }

    [SerializeField]
    private int PrivateField;
}

Here are the precise (default) rules:

  • Public fields are serialized by default
  • Auto-properties that are at least partially public are serialized by default
  • All fields or properties annotated with [SerializeField] or [fsProperty] are serialized
  • Public fields/public auto-properties are not serialized if annotated with [NonSerialized] or [fsIgnore]. [fsIgnore] can be used on properties (unlike [NonSerialized]).

Inheritance and cyclic object graphs are automatically handled by Full Serializer. You don't need to do anything.

Here's a simple example of how to use the Full Serializer API to serialize objects to and from strings.

using System;
using FullSerializer;

public static class StringSerializationAPI {
    private static readonly fsSerializer _serializer = new fsSerializer();

    public static string Serialize(Type type, object value) {
        // serialize the data
        fsData data;
        _serializer.TrySerialize(type, value, out data).AssertSuccessWithoutWarnings();

        // emit the data via JSON
        return fsJsonPrinter.CompressedJson(data);
    }

    public static object Deserialize(Type type, string serializedState) {
        // step 1: parse the JSON data
        fsData data = fsJsonParser.Parse(serializedState);

        // step 2: deserialize the data
        object deserialized = null;
        _serializer.TryDeserialize(data, type, ref deserialized).AssertSuccessWithoutWarnings();

        return deserialized;
    }
}

Note that this API example will throw exceptions if any errors occur. Additionally, error recovery is disabled in this example - if you wish to enable it, simply replace the AssertSuccessWithoutWarnings calls with AssertSuccess.

Custom Serialization

Full Serializer allows you to easily customize how serialization happens, via [fsObject], fsConverter, and fsObjectProcessor.

Simple Customization with [fsObject]

You can easily customize the serialization of a specific object by utilizing [fsObject]. There are a number of options:

You can specify what the default member serialization is by changing the MemberSerialization parameter. The options are OptIn, OptOut, and Default. OptIn requires that every serialize member be annotated with fsProperty, OptOut will serialize every member except those annotated with fsIgnore, and Default uses the default intelligent behavior where visibility level and property type are examined.

You can also specify a custom converter or processor to use directly on the model. This is more efficient than registering a custom converter / processor on the fsSerializer instance and additionally provides portability w.r.t. the actual fsSerializer instance; the fsSerializer creator does not need to know about this specific converter / processor.

Advanced Customization with Converters

Converters (to/from JSON) enable complete customization over the serialization of an object. Each converter expresses interest in what types it wants to take serialization over; there are two methods to do this. The more powerful (but slower) method is present in fsConverter, which determines if it interested via a function callback, and fsDirectConverter, which directly specifies which type of object it will take over conversion for. The primary limitation for fsDirectConverter is that it does not handle inheritance.

Example

Suppose we have this model:

public struct Id {
    public int Identifier;
}

We want to serialize this Id instance directly to an integer. Normally this is difficult, but with converters it is doable.

For example, we want to serialize new Id { Identifier = 3 } to 3.

Let's take a look at the converter which will handle this:

public class IdConverter : fsDirectConverter {
    public override Type ModelType { get { return typeof(Id); } }

    public override object CreateInstance(fsData data, Type storageType) {
        return new Id();
    }

    public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType) {
        serialized = new fsData(((Id)instance).Identifier);
        return fsResult.Success;
    }

    public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType) {
        if (data.IsInt64 == false) return fsResult.Fail("Expected int in " + data);

        instance = new Id { Identifier = (int)data.AsInt64 };
        return fsResult.Success;
    }
}

The converter is fairly straight-forward. ModelType maps to the type of object this converter applies to. It is used when you register a converter using either the fsConverterRegistrar or AddConverter.

CreateInstance allocates the actual object instance. If you're curious why this method is separate from TryDeserialize, then rest assured knowing that it is because cyclic object graphs require deserializing and object instance allocation to be separated (otherwise deserialization would enter into an infinite loop).

TrySerialize serializes the object instance. instance is guaranteed to be an instance of Id (or rather whatever CreateInstance returned), and storageType is guaranteed to be equal to typeof(Id).

TryDeserialize deserializes the json data into the object instance.

What's up with all of this fsResult stuff? Quite simply, fsResult is used so that Full Serializer doesn't have to use exceptions. Errors and problems happen when (typically when deserializing) - the fsResult instance can contain warning information or error information. If there is an error, then that almost certainly means that you want to stop deserialization, but for a warning you should keep going. You can ignore errors or treat them as warnings if your converter supports partial deserialization.

You may be curious what happens if we try to serialize, say, this object graph:

class Hello {
    public object field;
}

Serialize(new Hello { field = new Id() });

Rest assured knowning that it serializes correctly and as expected, even though we have a custom converter. Full Serializer takes care of these type of details so you don't have to worry about it.

Converter Registration

There are three ways to register a converter.

  • If you have access to the model type itself, then you can simply add an [fsObject] annotation. This registration method is static and cannot be modified at runtime. Here's an example:
[fsObject(Converter = typeof(IdConverter))]
public struct Id {
    public int Identifier;
}
  • If you don't have access to the model type or the serializer, then you can register the converter using fsConverterRegistrar. This registration method is static and cannot be modified at runtime. Here's an example:
namespace FullSerializer {
    partial class fsConverterRegistrar {
        // Method 1: Via a field
        // Important: The name *must* begin with Register
        public static IdConverter Register_IdConverter;

        // Method 2: Via a method
        // Important: The name *must* begin with Register
        public static void Register_IdConverter() {
            // do something here, ie:
            Converters.Add(typeof(IdConverter));
        }
    }
}
  • If you have access to the fsSerializer instance, then you can dynamically determine which converters to register. For example:
void CreateSerializer() {
    var serializer = new fsSerializer();
    serializer.AddConverter(new IdConverter());
}

Full Converter Example

Here's the full example:

using System;
using FullSerializer;

[fsObject(Converter = typeof(IdConverter))]
public struct Id {
    public int Identifier;
}

public class IdConverter : fsDirectConverter {
    public override Type ModelType { get { return typeof(Id); } }

    public override object CreateInstance(fsData data, Type storageType) {
        return new Id();
    }

    public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType) {
        serialized = new fsData(((Id)instance).Identifier);
        return fsResult.Success;
    }

    public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType) {
        if (data.IsInt64 == false) return fsResult.Fail("Expected int in " + data);

        instance = new Id { Identifier = (int)data.AsInt64 };
        return fsResult.Success;
    }
}

Another Converter Example

Here's an example converter, with lots of comments to explain things as you read:

using System;
using FullSerializer;

// We're going to serialize MyType directly to/from a string.
[fsObject(Converter = typeof(MyTypeConverter))]
public class MyType {
    public string Value;
}

pu

Related Skills

View on GitHub
GitHub Stars1.1k
CategoryCustomer
Updated6d ago
Forks171

Languages

C#

Security Score

95/100

Audited on Mar 18, 2026

No findings