JSONEncoder处理泛型协议包装类型时nil被编码为null的解决问询
解决Swift中JSONEncoder泛型包装类型nil值被编码为null的问题
问题回顾
直接编码ClickEvent结构体时,nil的contents字段会被JSONEncoder自动跳过,输出{"name":"click"};但通过EventWrapper泛型包装后编码,会输出{"name":"click","data":null}。核心原因是:
ClickEvent的contents是可选类型(Int?),JSONEncoder默认跳过nil的可选属性EventWrapper中的data类型为T.Contents,对于ClickEvent来说就是Int?,但该属性本身被声明为非可选的(只是它的类型是可选型),因此编码器会将整个nil值编码为null而非跳过字段
由于无法修改现有类型层级、后端拒绝接收null值,且JSONEncoder无全局配置跳过null的选项,以下是两种可行的解决办法:
解决办法1:通过Mirror反射判断Optional是否为nil
给EventWrapper添加自定义encode(to:)方法,利用Mirror反射判断data是否为nil的Optional类型,若是则不编码data字段:
struct EventWrapper<T: Event>: Encodable { let name: String let data: T.Contents init(event: T) { name = event.name data = event.contents } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) // 反射检查data是否为nil的Optional let mirror = Mirror(reflecting: data) if mirror.displayStyle == .optional && mirror.children.isEmpty { return // 不编码data字段 } try container.encode(data, forKey: .data) } enum CodingKeys: String, CodingKey { case name, data } }
解决办法2:利用自定义协议判断Optional是否为nil
定义一个私有协议标记所有Optional类型,通过协议方法判断data是否为nil,从而决定是否编码data字段,这种方式更优雅且性能优于反射:
struct EventWrapper<T: Event>: Encodable { let name: String let data: T.Contents init(event: T) { name = event.name data = event.contents } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) // 判断data是否为nil的Optional if let optionalData = data as? AnyOptional, optionalData.isNil { return } try container.encode(data, forKey: .data) } enum CodingKeys: String, CodingKey { case name, data } } // 私有协议,用于判断任意Optional是否为nil private protocol AnyOptional { var isNil: Bool { get } } extension Optional: AnyOptional { var isNil: Bool { self == nil } }
验证结果
修改后重新编码EventWrapper实例,输出将变为:
{"name":"click"}
内容的提问来源于stack exchange,提问作者Cristik
相关产品推荐
相关产品推荐

