You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何用JsonUtility.ToJson序列化及反序列化System.Action?

解决Unity中JsonUtility序列化/反序列化System.Action的问题

Unity自带的JsonUtility无法直接序列化或反序列化System.Action委托——因为委托本质是方法的引用,不属于可被JSON存储的数值或结构化数据类型。要实现这个需求,必须通过间接存储方法标识信息+反射重建委托的方式来处理。

解决方案步骤

1. 修改EnvironmentAction类,添加序列化支撑逻辑

新增可序列化字段存储方法的类名和方法名,同时通过ISerializationCallbackReceiver接口处理序列化前后的逻辑转换:

using UnityEngine;
using System;
using System.Reflection;

[Serializable]
public class EnvironmentAction : ISerializationCallbackReceiver
{
    // 用于JSON序列化的字段:存储方法所属类的全名、方法名
    [SerializeField] private string _className;
    [SerializeField] private string _methodName;

    // 实际业务使用的委托,标记为NonSerialized避免JsonUtility处理
    [NonSerialized] private Action _actionToDoNoParameters;

    // 构造函数
    public EnvironmentAction(Action actionToDo)
    {
        _actionToDoNoParameters = actionToDo;
    }

    public void Execute()
    {
        _actionToDoNoParameters?.Invoke();
    }

    // 序列化前回调:从委托中提取方法标识信息
    public void OnBeforeSerialize()
    {
        if (_actionToDoNoParameters == null)
        {
            _className = null;
            _methodName = null;
            return;
        }

        MethodInfo method = _actionToDoNoParameters.Method;
        _className = method.DeclaringType.FullName;
        _methodName = method.Name;
    }

    // 反序列化后回调:通过反射重建委托
    public void OnAfterDeserialize()
    {
        if (string.IsNullOrEmpty(_className) || string.IsNullOrEmpty(_methodName))
        {
            _actionToDoNoParameters = null;
            return;
        }

        // 获取目标类型
        Type targetType = Type.GetType(_className);
        if (targetType == null)
        {
            Debug.LogError($"找不到目标类型:{_className}");
            return;
        }

        // 获取目标方法(包含公共、私有、静态、实例方法)
        MethodInfo method = targetType.GetMethod(_methodName, 
            BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
        if (method == null)
        {
            Debug.LogError($"类型 {_className} 中找不到方法:{_methodName}");
            return;
        }

        // 重建委托:区分静态方法和实例方法
        if (method.IsStatic)
        {
            _actionToDoNoParameters = (Action)Delegate.CreateDelegate(typeof(Action), method);
        }
        else
        {
            // 实例方法需要获取对应的对象实例,这里以单例为例
            // 你可以根据实际场景调整实例获取逻辑(比如通过InstanceID查找场景对象)
            object instance = targetType.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static)?.GetValue(null);
            if (instance == null)
            {
                Debug.LogError($"无法获取 {_className} 的实例,无法绑定实例方法 {_methodName}");
                return;
            }
            _actionToDoNoParameters = (Action)Delegate.CreateDelegate(typeof(Action), instance, method);
        }
    }
}

2. 核心逻辑说明

  • 序列化阶段:调用JsonUtility.ToJson()前,OnBeforeSerialize自动触发,从委托中提取目标方法的类全名和方法名,存储到可序列化的字符串字段中,这些字段会被正常序列化为JSON内容。
  • 反序列化阶段:调用JsonUtility.FromJson()后,OnAfterDeserialize自动触发,通过反射根据存储的类名和方法名找到对应的方法,重新创建委托绑定到_actionToDoNoParameters字段。

3. 注意事项与限制

  • 匿名方法/Lambda表达式无法序列化:这类方法没有固定的方法名,反射无法定位,必须使用具名的静态或实例方法。
  • 实例方法的实例获取:如果绑定的是实例方法,必须确保反序列化时能获取到对应的对象实例。比如单例类可以通过单例属性获取;场景中的MonoBehaviour可以保存对象的InstanceID,反序列化时用FindObjectOfInstanceID查找。
  • 方法访问权限:反射时指定了BindingFlags.NonPublic,支持序列化私有方法。

测试代码示例

using UnityEngine;
using System.IO;

public class ActionSerializationTest : MonoBehaviour
{
    private EnvironmentAction _environmentAction;

    void Start()
    {
        // 绑定静态方法
        _environmentAction = new EnvironmentAction(TestStaticMethod);
        // 绑定单例实例方法示例
        // _environmentAction = new EnvironmentAction(TestSingleton.Instance.TestInstanceMethod);
    }

    private static void TestStaticMethod()
    {
        Debug.Log("执行静态测试方法");
    }

    private void OnApplicationQuit()
    {
        // 序列化到JSON文件
        string json = JsonUtility.ToJson(_environmentAction);
        File.WriteAllText(@".\test_action.json", json);

        // 反序列化测试
        EnvironmentAction deserializedAction = JsonUtility.FromJson<EnvironmentAction>(json);
        deserializedAction.Execute(); // 执行后会输出对应的日志
    }
}

// 单例示例类
public class TestSingleton
{
    private static TestSingleton _instance;
    public static TestSingleton Instance => _instance ??= new TestSingleton();

    public void TestInstanceMethod()
    {
        Debug.Log("执行单例实例测试方法");
    }
}

内容的提问来源于stack exchange,提问作者nathan raynal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 23:40:31