C#中Stack<T>类型属性的Json反序列化问题咨询
嘿,我知道你遇到Stack
你这段代码里,反序列化后Cards属性的元素大概率会出现顺序反转的情况,甚至可能不符合预期?这其实是Json.NET处理Stack的特性导致的:
Stack
另外,你的Game类里Cards是只读自动属性(只有getter),虽然Json.NET可以往你初始化好的Stack实例里添加元素,但因为Stack的特殊性,还是会踩顺序的坑。
解决方案
方案1:自定义JsonConverter处理Stack(推荐)
写一个自定义转换器,让序列化和反序列化时保持顺序一致。这个转换器会在序列化时把Stack转成List(按实际存储的底部到顶部顺序),反序列化时再把List转成正确顺序的Stack:
public class StackConverter<T> : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(Stack<T>); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 把JSON数组反序列化成List var list = serializer.Deserialize<List<T>>(reader); // 反转List后再创建Stack,保证顺序和原Stack一致 return new Stack<T>(list.AsEnumerable().Reverse()); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var stack = (Stack<T>)value; // 把Stack转成List并反转,让序列化后的数组和Stack实际存储顺序一致 var list = stack.ToList(); list.Reverse(); serializer.Serialize(writer, list); } }
然后给Game类的Cards属性加上转换器标记:
public class Game { [JsonConverter(typeof(StackConverter<Card>))] public Stack<Card> Cards { get; } = new Stack<Card>(); }
这样序列化和反序列化后,Stack的元素顺序就和原来完全一致了。
方案2:改用List封装Stack行为(简单易维护)
如果不想写自定义转换器,可以把内部存储换成List
public class Game { private readonly List<Card> _cards = new List<Card>(); // 对外提供Stack的核心操作 public void PushCard(Card card) => _cards.Add(card); public Card PopCard() { if (_cards.Count == 0) throw new InvalidOperationException("没有剩余卡牌了"); var card = _cards[_cards.Count - 1]; _cards.RemoveAt(_cards.Count - 1); return card; } // 让Json.NET序列化这个List [JsonProperty("Cards")] private List<Card> CardsList => _cards; }
这种方式不需要额外写转换器,避免了Stack序列化的坑,但需要调整类的结构,适合可以修改类定义的场景。
方案3:临时手动转换(适合一次性场景)
如果只是偶尔处理这个对象,也可以在序列化前把Stack转成List,反序列化后再转成Stack,不需要修改类:
// 序列化时:把Stack转成List再序列化 var ser = JsonConvert.SerializeObject(new { Cards = game.Cards.ToList() }); // 反序列化时:先转成List,再反转后Push回Stack var temp = JsonConvert.DeserializeAnonymousType(ser, new { Cards = new List<Card>() }); var unser = new Game(); foreach (var card in temp.Cards.Reverse()) { unser.Cards.Push(card); }
这种方式灵活,但每次处理都要手动写转换逻辑,适合临时场景。
内容的提问来源于stack exchange,提问作者Ole Albers

