使用System.Text.JSON实现含数组的类型安全类序列化的设计模式
是否要为序列化易用性牺牲类型安全?
不需要为了序列化易用性牺牲类型安全,有几种不依赖自定义JSON转换器和特性标记的设计方案可以兼顾两者:
方案1:引入数据传输对象(DTO)
- 定义一个与
Polynomial结构对应的只读DTO类,仅包含序列化所需的核心数据:public record PolynomialDto(int MaxOrder, Dictionary<int, double> CoefficientValues); - 在
Polynomial类中添加双向转换方法,实现领域模型与DTO的映射:public class Polynomial { // 原有类型安全的代码保持不变 public PolynomialDto ToDto() { var values = Coefficients.ToDictionary(c => c.Exponent, c => c.Value); return new PolynomialDto(MaxOrder, values); } public static Polynomial FromDto(PolynomialDto dto) { var polynomial = new Polynomial(dto.MaxOrder); foreach (var (exponent, value) in dto.CoefficientValues) { if (exponent >= 0 && exponent <= dto.MaxOrder) { polynomial.Coefficients[exponent].Value = value; } // 可按需处理非法指数,比如抛出异常或忽略 } return polynomial; } } - 序列化/反序列化时只操作DTO对象,完全隔离领域模型与序列化逻辑,既保留类型安全,又避免技术耦合。
方案2:重构内部存储与访问逻辑
- 将
Coefficients改为私有字段,对外提供只读访问接口,同时新增一个专用于序列化的属性:public class Polynomial { private readonly PolynomialCoefficient[] _coefficients; public Polynomial(int maxOrder) { MaxOrder = maxOrder; _coefficients = new PolynomialCoefficient[maxOrder + 1]; for (int i = 0; i <= maxOrder; i++) { _coefficients[i] = new PolynomialCoefficient(i); } } public int MaxOrder { get; } // 对外暴露只读的系数列表,保证类型安全 public IReadOnlyList<PolynomialCoefficient> Coefficients => _coefficients; // 序列化专用属性,内部自动验证指数合法性 public Dictionary<int, double> CoefficientValues { get => _coefficients.ToDictionary(c => c.Exponent, c => c.Value); set { if (value == null) return; foreach (var (exponent, val) in value) { if (exponent >= 0 && exponent <= MaxOrder) { _coefficients[exponent].Value = val; } } } } } - 这种方式下,外部无法修改系数数组的结构,类型安全不受影响;序列化时通过
CoefficientValues完成数据读写,且内部会自动过滤非法指数,避免出现数组与MaxOrder不兼容的问题。
方案3:用工厂模式接管反序列化流程
- 保留
Polynomial的只读特性,将反序列化逻辑转移到独立的工厂类中:public static class PolynomialFactory { public static Polynomial CreateFromSerializedData(int maxOrder, Dictionary<int, double> coefficientValues) { var polynomial = new Polynomial(maxOrder); foreach (var (exponent, value) in coefficientValues) { if (exponent < 0 || exponent > maxOrder) { throw new ArgumentOutOfRangeException(nameof(exponent), $"指数必须在0到{maxOrder}之间"); } polynomial.Coefficients[exponent].Value = value; } return polynomial; } } - 序列化时仅输出
MaxOrder和系数键值对,反序列化时先将JSON转为基础类型(如匿名对象或字典),再通过工厂方法创建合法的Polynomial实例,全程不破坏原有模型的类型安全设计。
内容的提问来源于stack exchange,提问作者tmren
相关产品推荐
相关产品推荐

