Swift Codable如何编码包装类型避免容器中产生JSON嵌套
问题描述
常规场景下定义如下结构体:
struct Box: Codable { let id: String /// 该字段为表达式(例如 `x + 3`),而非普通数值 let height: String }
编码后输出的JSON格式为:
{ "id": "box1", "height": "x + 3" }
为了明确标识height字段不能传入普通字符串,现在引入新的包装类型Expression,修改后的代码如下:
struct Box: Codable { let id: String let height: Expression } struct Expression: Codable { let string: String }
引入包装类型后API语义更清晰,但编码生成的JSON出现了不必要的嵌套:
{ "id": "box1", "height": { "string": "x + 3" } }
需要消除这层嵌套,让编码结果恢复为扁平化结构:
{ "id": "box1", "height": "x + 3" }
实现约束
不能重写Box类型的任何Codable相关逻辑,原因如下:
Box可能包含大量其他属性,手动维护encode/decode函数的维护成本过高- 所有使用
Expression类型的场景都应自动复用无嵌套的编码行为,无需重复编写Codable样板代码
也就是说需求范围限定为:仅修改Expression类型的实现,使其编码输出时不引入额外的容器层级。
此前尝试过使用container.superEncoder()实现,但该方法仅会将嵌套键名从"string"修改为"super",层级嵌套的问题并未解决。
实现方案
给Expression实现自定义Codable逻辑,使用单值容器替代默认的键值容器即可,代码如下:
struct Expression: Codable { let string: String init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() string = try container.decode(String.self) } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(string) } }
实现原理:系统为结构体自动生成的Codable实现,默认会创建一个键值映射容器,将结构体的所有属性作为嵌套键写入该容器,这就是多余嵌套的来源。而
singleValueContainer()会直接在当前编码位置读写原始值,不会额外创建嵌套容器,完全匹配需求。该实现不需要修改
Box的任何代码,所有引用Expression类型的地方都会自动应用扁平化的编解码逻辑,没有额外的重复维护成本。
内容的提问来源于stack exchange,提问作者Senseful
相关产品推荐
相关产品推荐

