C#使用BinaryFormatter序列化时第三方Point类无Serializable属性如何解决
解决方案
你可以通过以下几种方式解决无法给第三方类型加[Serializable]特性的序列化问题:
方案1:为ServerItem实现ISerializable接口自定义序列化逻辑
自己控制ServerItem的序列化过程,手动存储Point的坐标值,反序列化时再重建Point对象:
[Serializable] public class ServerItem : ISerializable { public string Id { get; } public string Name => this.info.Name; public bool IsOptimal { get; set; } public string Region => this.info.Region; public string City => this.info.Description; public Point? Location { get; } // 反序列化用的构造函数 protected ServerItem(SerializationInfo info, StreamingContext context) { Id = info.GetString(nameof(Id)); IsOptimal = info.GetBoolean(nameof(IsOptimal)); // 反序列化Point坐标 if (info.GetBoolean("HasLocation")) { double x = info.GetDouble("LocationX"); double y = info.GetDouble("LocationY"); Location = new Point(x, y); } // 这里补充info字段的反序列化逻辑,根据你实际的info类型处理 } // 自定义序列化逻辑 public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue(nameof(Id), Id); info.AddValue(nameof(IsOptimal), IsOptimal); // 单独存Point的坐标 info.AddValue("HasLocation", Location.HasValue); if (Location.HasValue) { info.AddValue("LocationX", Location.Value.X); info.AddValue("LocationY", Location.Value.Y); } // 这里补充info字段的序列化逻辑,根据你实际的info类型处理 } // 你原来的其他构造函数、逻辑保留 }
方案2:新增可序列化的坐标存储字段,将Point改为计算属性
无需修改序列化逻辑,只需要调整ServerItem的字段设计:
[Serializable] public class ServerItem { public string Id { get; } public string Name => this.info.Name; public bool IsOptimal { get; set; } public string Region => this.info.Region; public string City => this.info.Description; // 用基础类型存储坐标,基础类型天然支持序列化 private double? _locationX; private double? _locationY; // Point改为计算属性,不参与默认序列化 public Point? Location => _locationX.HasValue ? new Point(_locationX.Value, _locationY.Value) : null; // 你原来的构造函数中,给Location赋值的逻辑改成给两个字段赋值 public ServerItem(/* 你的其他参数 */ Point? location) { if (location.HasValue) { _locationX = location.Value.X; _locationY = location.Value.Y; } } // 其他原有逻辑保留 }
方案3:为Point类型注册序列化代理
不需要修改ServerItem类,只需要调整你的序列化方法,给BinaryFormatter指定Point的序列化规则:
首先实现Point的序列化代理类:
public class PointSerializationSurrogate : ISerializationSurrogate { public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) { Point point = (Point)obj; info.AddValue("X", point.X); info.AddValue("Y", point.Y); } public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { double x = info.GetDouble("X"); double y = info.GetDouble("Y"); return new Point(x, y); } }
然后修改你的序列化方法,注册代理:
public static byte[] Serialize(object obj) { try { byte[] bytes; IFormatter formatter = new BinaryFormatter(); // 注册Point的序列化代理 SurrogateSelector surrogateSelector = new SurrogateSelector(); surrogateSelector.AddSurrogate(typeof(Point), new StreamingContext(StreamingContextStates.All), new PointSerializationSurrogate()); formatter.SurrogateSelector = surrogateSelector; using (MemoryStream stream = new MemoryStream()) { formatter.Serialize(stream, obj); bytes = stream.ToArray(); } return bytes; } catch { return null; } } // 反序列化方法也要加上同样的代理注册逻辑
方案4:替换序列化方案(更推荐)
BinaryFormatter已经被微软标记为不安全API,高版本.NET默认禁用,存在远程代码执行风险。你可以换成System.Text.Json、Newtonsoft.Json或者Protobuf.NET这类序列化库,这类库不需要类型标记[Serializable]特性,只要可以访问到公共属性就可以直接序列化,改造成本更低,安全性和性能也更好。
内容的提问来源于stack exchange,提问作者Янислав Колодочка
相关产品推荐
相关产品推荐

