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

如何用Json.NET自定义序列化与反序列化R3的ReactiveProperty泛型类型

问题描述

使用R3响应式编程库与Newtonsoft.Json实现对象数据的文件存储与读取,序列化过程可生成仅保留ReactiveProperty的Value字段的精简JSON,但反序列化时抛出错误:

Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: {. Path 'resourceList[0].Count', line 5, position 16.

不清楚如何在Json.NET中处理ReactiveProperty<>这类泛型类型,纠结使用JsonConverter还是ContractResolver方案。

相关代码如下:

public class Test : MonoBehaviour
{
    private void Start()
    {
        TestData testData = new TestData();

        DataStorageAgent dataStorageAgent = new DataStorageAgent();
        string savePath = Application.dataPath + "/Saves/Test.json";

        dataStorageAgent.Save(testData, savePath);
        testData = dataStorageAgent.Load<TestData>(savePath);
    }
}
public class TestData
{
    public List<Resource> resourceList;
    public ObservableList<Resource> observableResourceList;
    public ReactiveProperty<int> number;
    public ReactiveProperty<string> word;

    public TestData()
    {
        resourceList = new List<Resource>();
        resourceList.Add(new Resource("banana", 10));
        resourceList.Add(new Resource("apple", 3));

        observableResourceList = new ObservableList<Resource>();
        observableResourceList.Add(new Resource("oBanana", 5));
        observableResourceList.Add(new Resource("oApple", 2));

        number = new ReactiveProperty<int>(9);
        word = new ReactiveProperty<string>("word string");
    }
}
public class Resource
{
    public readonly string Name;
    public ReactiveProperty<int> Count;

    public Resource(string name, int count)
    {
        Name = name;
        Count = new ReactiveProperty<int>(count);
    }
}
public class DataStorageAgent
{
    public T Load<T>(string saveFilePath) where T : class
    {
        if (File.Exists(saveFilePath))
        {
            string jsonString = File.ReadAllText(saveFilePath);

            try
            {
                return JsonConvert.DeserializeObject<T>(jsonString, new ReactivePropertyJsonConverter());
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
        }
        else
        {
            Debug.LogError($"File does not exist - {saveFilePath}");
        }

        return default(T);
    }

    public void Save(object saveData, string saveFilePath)
    {
        string jsonString = JsonConvert.SerializeObject(saveData, Formatting.Indented, new ReactivePropertyJsonConverter());
        File.WriteAllText(saveFilePath, jsonString);
    }
}
public class ReactivePropertyJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(ReactiveProperty<>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        //JObject jsonObject = JObject.Load(reader);
        //return existingValue;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        writer.WritePropertyName("Value");
        serializer.Serialize(writer, value.GetType().GetProperty("Value").GetValue(value));
        writer.WriteEndObject();
    }
}
解决方案

核心问题在于你的ReactivePropertyJsonConverter仅实现了序列化(WriteJson)逻辑,反序列化(ReadJson)方法为空,导致Json.NET无法正确解析JSON并创建ReactiveProperty<>实例。以下是完整修复方案:

1. 完善ReactivePropertyJsonConverter的反序列化逻辑

修改转换器,实现ReadJson方法,读取JSON中的Value字段并创建对应的泛型实例:

public class ReactivePropertyJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(ReactiveProperty<>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // 读取JSON对象并提取Value字段
        var jObject = JObject.Load(reader);
        var valueToken = jObject.GetValue("Value", StringComparison.OrdinalIgnoreCase);
        
        // 获取ReactiveProperty的泛型参数类型
        var valueType = objectType.GetGenericArguments()[0];
        // 解析Value为对应类型
        var value = serializer.Deserialize(valueToken.CreateReader(), valueType);
        
        // 通过反射创建ReactiveProperty实例并传入初始值
        return Activator.CreateInstance(objectType, value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        writer.WritePropertyName("Value");
        var valueProperty = value.GetType().GetProperty("Value");
        serializer.Serialize(writer, valueProperty.GetValue(value));
        writer.WriteEndObject();
    }
}

2. 处理ObservableList<>的序列化(可选)

若需精简ObservableList<>的序列化结果(默认包含R3内部冗余字段),可添加对应转换器:

public class ObservableListJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(ObservableList<>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var itemType = objectType.GetGenericArguments()[0];
        var listType = typeof(List<>).MakeGenericType(itemType);
        var list = serializer.Deserialize(reader, listType);
        
        // 创建ObservableList并传入解析后的列表
        return Activator.CreateInstance(objectType, list);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // 直接序列化ObservableList的内部集合
        var itemsProperty = value.GetType().GetProperty("Items");
        serializer.Serialize(writer, itemsProperty.GetValue(value));
    }
}

使用时同时传入两个转换器:

// Save方法
string jsonString = JsonConvert.SerializeObject(saveData, Formatting.Indented, 
    new ReactivePropertyJsonConverter(), new ObservableListJsonConverter());

// Load方法
return JsonConvert.DeserializeObject<T>(jsonString, 
    new ReactivePropertyJsonConverter(), new ObservableListJsonConverter());

3. 修复Resource类的反序列化

由于Resource类只有带参构造函数,需添加[JsonConstructor]标签指定Json.NET使用该构造函数:

public class Resource
{
    public readonly string Name;
    public ReactiveProperty<int> Count;

    [JsonConstructor]
    public Resource(string name, int count)
    {
        Name = name;
        Count = new ReactiveProperty<int>(count);
    }
}

方案选择:JsonConverter vs ContractResolver

对于ReactiveProperty<>这类需要自定义序列化/反序列化逻辑的泛型类型,JsonConverter是更合适的方案:

  • 逻辑集中独立,每个类型的序列化规则封装在单独类中
  • 泛型类型的判断和处理更灵活
  • 无需修改全局契约解析规则,避免影响其他类型的序列化行为
验证结果

修改后,序列化生成的JSON仅保留ReactiveProperty的Value字段和ObservableList的实际元素,反序列化时可正确创建所有对象实例,不再抛出解析错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 21:55:11