泛型容器可选类型nil值JSON编码不省略的解决方法问询
泛型Codable类型处理nil值序列化的问题
当使用JSONEncoder对包含可选属性的Encodable类型执行编码时,nil值会被自动省略,最终生成空的JSON结构,以下测试用例可验证该行为:
func test_whenNilValueIsSerialised_thenItIsOmitted() throws { struct Container: Codable { let cargo: Int? } let encoding = try XCTUnwrap(String(bytes: JSONEncoder().encode(Container(cargo: nil)), encoding: .utf8)) XCTAssertEqual(encoding, "{}") }
但如果是泛型Codable类型,且将可选类型作为泛型参数传入时,上述自动省略nil的行为失效,以下测试用例会执行失败:
func test_whenNilValueFromGenericIsSerialised_thenItIsOmitted() throws { struct Container<Cargo: Codable>: Codable { let cargo: Cargo } let container = Container<String?>(cargo: nil) let encoding = try XCTUnwrap(String(bytes: JSONEncoder().encode(container), encoding: .utf8)) XCTAssertEqual(encoding, "{}") }
失败报错信息:
XCTAssertEqual failed: ("{"cargo":null}") is not equal to ("{}")
问题:是否存在简洁的解决方案?比如为Container实现显式的encode(to:)方法?
注:直接将属性定义为let cargo: Cargo?的方案不适用,因为多数场景下需要使用非可选类型作为Cargo参数。
解决方案:自定义encode(to:)方法
可以通过实现自定义的encode(to:)方法,在编码时判断cargo是否为nil(仅当Cargo为可选类型时),若为nil则跳过该字段,否则正常编码。
struct Container<Cargo: Codable>: Codable { let cargo: Cargo func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) // 识别可选类型的nil值并跳过编码 switch cargo { case Optional<Any>.none: break default: try container.encode(cargo, forKey: .cargo) } } enum CodingKeys: String, CodingKey { case cargo } }
修改后,上述失败的测试用例会顺利通过:当Cargo是可选类型且值为nil时,cargo字段会被省略,生成{};当Cargo是非可选类型时,会正常编码该字段。
如果需要更严谨的类型检查,也可以通过Mirror或类型判断来确认Cargo为可选类型后再处理,但上述switch的方式已经能覆盖绝大多数场景。
内容的提问来源于stack exchange,提问作者Benjohn
相关产品推荐
相关产品推荐

