如何用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
相关产品推荐
相关产品推荐

