TeamSirenix / odin-serializer

Fast, robust, powerful and extendible .NET serializer built for Unity
http://www.odininspector.com
Apache License 2.0
1.69k stars 193 forks source link

(AOT) No serializer for C# struct (readonly or not) is pre-generated #27

Closed laicasaane closed 5 years ago

laicasaane commented 5 years ago

Because my project uses structs and readonly structs heavily, so I have made a test (WebGL build) to see if OdinSerializer can work on them. However, there is no serializer for struct of any kind is pre-generated.

The test struct:

[Serializable]
public readonly struct MyData
{
    public readonly int Id;
    public readonly string Name;
    public readonly Action OnDidSomething;

    public MyData(int id, string name, Action onDidSomething)
    {
        this.Id = id;
        this.Name = name;
        this.OnDidSomething = onDidSomething;
    }
}

The result is a little unexpected, since there is no such problem with classes. I've made another test with OdinInspector 2.1.1, the problem is the same, and I have to manually input MyData into the "Support Serialized Types" list.

TorVestergaard commented 5 years ago

That's not very meaningful on its own, I'm afraid. Whether AOT support is generated for it depends on whether the scan picks it up - and the default scan will only pick up all needed types for data serialized by Odin Serializer which is stored in scenes or other assets that will end up in the final build. There's no easy way to scan the code base and predict which types you will need support for; we can only go by the data in assets. In this case, for example, if MyData is serialized by Unity and not Odin, then the scan will not pick it up.

If you're sure it actually is a bug, please include precise reproduction steps and versions of everything, so I can independently reproduce the behaviour.

laicasaane commented 5 years ago

There's no easy way to scan the code base and predict which types you will need support for; we can only go by the data in assets.

In my case, I only want to use OdinSerializer instead of Json.NET to serialize and deserialize json data. Currently there is no need for assets or custom Unity objects.

In this case, for example, if MyData is serialized by Unity and not Odin, then the scan will not pick it up.

If I change MyData into class, then there won't be any problem. Because I only decorate MyData with SerializableAttribute, how could Odin know if MyData is serialized by Unity or Odin then? Well, I really don't understand this case, so for me it seems to be a bug.

The production step is quite simple:

  1. Create a project with OdinSerializer plugin (built from the current master branch).
  2. Use the OdinSerializerTest.cs as described below.
  3. Build for WebGL with Scripting Runtime = .NET 4.x, API Compatibility Level = .NET Standard 2.0
  4. Run the "index.html" and see the browser's Console if it prints the text "Do Something" or throws AOT errors.

OdinSerializerTest.cs

using System;
using UnityEngine;
using OdinSerializer;

namespace Test
{
    public class OdinSerializerTest : MonoBehaviour
    {
        public void Start()
        {
            var myData = new MyData(5, "A new name", Helper.DoSomething);

            var outData = SerializationUtility.SerializeValue(myData, DataFormat.JSON);
            var outStr = System.Text.Encoding.Default.GetString(outData);

            var inData = System.Text.Encoding.Default.GetBytes(outStr);
            var value = SerializationUtility.DeserializeValue<MyData>(inData, DataFormat.JSON);

            value.OnDidSomething?.Invoke();
        }
    }

    [Serializable]
    public readonly struct MyData
    {
        public readonly int Id;
        public readonly string Name;
        public readonly Action OnDidSomething;

        public MyData(int id, string name, Action onDidSomething)
        {
            this.Id = id;
            this.Name = name;
            this.OnDidSomething = onDidSomething;
        }
    }

    public static class Helper
    {
        public static void DoSomething()
        {
            Debug.Log("Do Something");
        }
    }
}
laicasaane commented 5 years ago

With OdinInspector, I can input MyData into the "Support Serialized Types" list and the build automation could generate AOT serializer for it. I wonder if we could have something like that for OdinSerializer, like register the type via some API so the build automation can know which type needs an AOT serializer?

TorVestergaard commented 5 years ago

Alright, going by that code example, this is not a bug, but just how things work - it confuses me a bit that it would work with a class - that sounds more like the bug. Though, actually, in some versions of the IL2CPP runtime I know that can work with reference types.

Odin needs the AOT support generated, and the scan works as I described it - in your case, the scan will not have a chance of detecting those types are needed. You can customize the AOT support generation yourself with Odin Serializer (and with Odin Inspector), using the AOTSupportUtilities and AOTSupportScanner classes. You can check out this part here, and also read some of our segments in the manual, to get a better understanding of how it all works.