如何使用JSON.NET反序列化时为嵌套类传递外部类引用?
如何用JSON.NET反序列化嵌套类并让内部类获取外部类引用?
这个问题我之前也碰到过,JSON.NET默认反序列化的时候确实不会自动处理这种嵌套类的外部引用,不过有两种比较实用的解决办法,我给你详细说下:
首先先修正下你给出的类结构(原代码里的_outerRef没声明类型,而且实际场景是List<inner>成员,另外JSON.NET反序列化需要无参构造):
public class outer { public int id; public List<inner> inners; // 实际场景的List类型成员 public class inner { private outer _outerRef; public inner() {} // 必须添加无参构造,否则JSON.NET无法直接反序列化 public inner(outer outerRef) { _outerRef = outerRef; } public int id; // 用表达式体写法更简洁 public string name => $"{id}.{_outerRef.id}"; } }
方法一:利用JSON.NET的OnDeserialized回调(最简单)
JSON.NET提供了序列化/反序列化的生命周期回调,我们可以在outer类里加一个反序列化完成后的方法,给每个inner实例设置外部引用:
只需要给outer类添加回调方法,同时给inner类加一个设置引用的方法:
public class outer { public int id; public List<inner> inners; public class inner { private outer _outerRef; public inner() {} public inner(outer outerRef) { _outerRef = outerRef; } public int id; public string name => $"{id}.{_outerRef.id}"; // 给内部类添加设置外部引用的方法 public void SetOuterRef(outer outer) { _outerRef = outer; } } // 标记这个方法在反序列化完成后触发 [Newtonsoft.Json.OnDeserialized] private void OnDeserialized(Newtonsoft.Json.Serialization.StreamingContext context) { // 遍历所有inner实例,设置外部引用 if (inners != null) { foreach(var innerItem in inners) { innerItem.SetOuterRef(this); } } } }
然后反序列化的时候直接用常规写法就行:
// 示例JSON字符串 var json = "{\"id\":5, \"inners\":[{\"id\":7}, {\"id\":8}]}"; var outerInstance = Newtonsoft.Json.JsonConvert.DeserializeObject<outer>(json); // 此时每个inner的name属性都能正常计算,比如第一个inner的name是"7.5"
方法二:自定义JsonConverter(更灵活)
如果你需要在反序列化inner的过程中就控制引用设置,或者有更复杂的逻辑,可以自定义一个转换器来处理outer类:
public class OuterConverter : Newtonsoft.Json.JsonConverter<outer> { public override outer ReadJson(JsonReader reader, Type objectType, outer existingValue, bool hasExistingValue, JsonSerializer serializer) { // 先把JSON加载成JObject,方便手动处理各个属性 var jsonObj = JObject.Load(reader); // 创建outer实例 var outerInstance = new outer(); // 反序列化outer的id属性 outerInstance.id = jsonObj["id"].Value<int>(); // 反序列化inners列表(此时inner实例还没有外部引用) outerInstance.inners = jsonObj["inners"].ToObject<List<outer.inner>>(serializer); // 给每个inner设置外部引用 foreach(var innerItem in outerInstance.inners) { innerItem.SetOuterRef(outerInstance); } return outerInstance; } public override void WriteJson(JsonWriter writer, outer value, JsonSerializer serializer) { // 序列化的时候用默认逻辑即可 serializer.Serialize(writer, value); } }
使用的时候需要把转换器加入序列化设置:
var jsonSettings = new Newtonsoft.Json.JsonSerializerSettings(); jsonSettings.Converters.Add(new OuterConverter()); var outerInstance = Newtonsoft.Json.JsonConvert.DeserializeObject<outer>(json, jsonSettings);
注意事项
- 一定要给
inner类加无参构造函数,JSON.NET默认会用无参构造创建实例,除非你在转换器里手动调用带参构造(但那样会增加复杂度)。 - 如果不想公开
SetOuterRef方法,可以把它设为internal,然后在项目属性里设置InternalsVisibleTo("Newtonsoft.Json"),或者用反射设置私有字段(不推荐,反射性能差且不利于维护)。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

