You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 18:20:29