如何在JSON反序列化与复杂工厂模式下避免重复数组
问题:移除Room类中的ItemStrings字段的最优实现方式
我正在开发一个文本冒险游戏及引擎项目,希望尽可能多的游戏内容用JSON编写,通过工厂链在启动时实例化游戏世界。当前代码中Room类同时包含Items和ItemStrings字段,希望移除ItemStrings,且保持调用方式Factory.MakeRoom("treasureRoom");不变。
现有代码示例:
现有Room类
public class Room{ public List<Item> Items; public List<string> ItemStrings; public string Description; public void AddRoom(Room room){ Items.Add(room); } }
现有Item类
public class Item{ string Name; string Description; }
现有JSON配置示例
treasureRoom.json
{ "ItemStrings": [ "pie", "shoe" ], "Description": "A Fancy Room" }
pie.json
{ "Name" : "pie", "Description": "its a pie" }
shoe.json
{ "Name" : "shoe", "Description": "its a shoe" }
现有工厂类
public static Room MakeRoom(string roomName) { var room = JsonConvert.DeserializeObject<Room>(Util.Readfile($"{basePath}/GameObjects/Rooms/{roomName}.json")); foreach(var item in room.ItemStrings) { room.AddItem(Factory.MakeItem(item)); } return room; } public static Item MakeItem(string fileName){ return JsonConvert.DeserializeObject<Item>(Util.Readfile($"{basePath}/GameObjects/Items/{itemName}.json")); }
最优实现方案
以下两种方案均无需修改现有JSON文件、保持Factory.MakeRoom调用方式不变,可彻底移除Room类的ItemStrings字段:
方案一:自定义JSON转换器(推荐)
通过自定义JsonConverter,在反序列化Room时直接处理JSON中的ItemStrings字段,自动加载并实例化对应Item,将逻辑封装在转换器中,让Room类更简洁。
步骤1:修改Room类,移除ItemStrings并修正方法
public class Room{ public List<Item> Items = new List<Item>(); // 初始化避免空引用异常 public string Description; // 修正原代码笔误:原AddRoom方法逻辑错误,应为AddItem public void AddItem(Item item){ Items.Add(item); } }
步骤2:实现Room自定义转换器
public class RoomConverter : JsonConverter<Room> { public override Room ReadJson(JsonReader reader, Type objectType, Room existingValue, bool hasExistingValue, JsonSerializer serializer) { var jsonObj = JObject.Load(reader); var room = new Room(); // 反序列化基础描述字段 room.Description = jsonObj["Description"]?.ToString(); // 解析ItemStrings并加载对应Item实例 var itemNames = jsonObj["ItemStrings"]?.ToObject<List<string>>(); if(itemNames != null) { foreach(var itemName in itemNames) { var item = Factory.MakeItem(itemName); room.AddItem(item); } } return room; } public override void WriteJson(JsonWriter writer, Room value, JsonSerializer serializer) { // 如需序列化Room,可反向将Items转为ItemStrings写入JSON var jsonObj = new JObject(); jsonObj["Description"] = value.Description; jsonObj["ItemStrings"] = new JArray(value.Items.Select(i => i.Name)); jsonObj.WriteTo(writer); } }
步骤3:修改MakeRoom方法使用转换器
public static Room MakeRoom(string roomName) { var jsonSettings = new JsonSerializerSettings(); jsonSettings.Converters.Add(new RoomConverter()); var jsonContent = Util.Readfile($"{basePath}/GameObjects/Rooms/{roomName}.json"); return JsonConvert.DeserializeObject<Room>(jsonContent, jsonSettings); }
方案二:DTO数据传输对象中转
定义一个与现有Room JSON结构完全匹配的DTO类,先反序列化到DTO,再映射为Room实例,隔离JSON结构与业务实体。
步骤1:定义RoomDTO类
public class RoomDTO{ public List<string> ItemStrings; public string Description; }
步骤2:修改Room类,移除ItemStrings
public class Room{ public List<Item> Items = new List<Item>(); public string Description; public void AddItem(Item item){ Items.Add(item); } }
步骤3:修改MakeRoom方法,用DTO中转
public static Room MakeRoom(string roomName) { var jsonContent = Util.Readfile($"{basePath}/GameObjects/Rooms/{roomName}.json"); var roomDTO = JsonConvert.DeserializeObject<RoomDTO>(jsonContent); var room = new Room(); room.Description = roomDTO.Description; foreach(var itemName in roomDTO.ItemStrings) { room.AddItem(Factory.MakeItem(itemName)); } return room; }
方案对比
- 方案一:逻辑封装性强,工厂方法更简洁,适合需要长期维护、后续可能扩展更多自定义反序列化逻辑的场景。
- 方案二:实现简单、代码量少,适合快速迭代、业务逻辑较简单的场景。
内容的提问来源于stack exchange,提问作者switch201
相关产品推荐
相关产品推荐

