swift中编码与解码有两个协议,Encodable和Decodable。
下面是编码与解码的示例
swift
import Foundation
// 编码
struct Person: Encodable {
let name: String
let age: Int
}
let person = Person(name: "lihua", age: 18)
let encodedData = try JSONEncoder().encode(person)
if let jsonsString = String(data: encodedData, encoding: .utf8){
print(jsonsString)
}
需要解释的是 编码通过JSONEncoder()编码器将Swift对象转为json格式,encode(person)将对象编码,但只是一种抽象,具体编码成什么,还需要看是什么编码器。
这里的二进制json数据不方便查看,所以需要转为字符串。
swift
//解码
struct Education: Decodable {
let classname: String
let school: String
}
let jsonString = """
{
"classname": "one",
"school": "first"
}
"""
if let jsonData = jsonString.data(using: .utf8){
let education = try JSONDecoder().decode(Education.self, from: jsonData)
print(education.classname)
} else{
print("解码失败")
}
这里我们将字符串的jsonString转为json,由于data方法返回的是一个可选型,所以需要可选绑定。这里使用if let

解码把data数据转换为Education的实例,这里decode方法的第一个参数是一个类型,返回一个实例对象,所以写成decode(Education.self, from: jsonData),Education.self表示类型本身,而Education表示对象

codable
这个协议是解码和编码的组合
swift
//codable
import Foundation
// ✅ 定义模型
struct Person2: Codable {
let name: String
let age: Int
}
// ✅ 模拟后端返回的 JSON 字符串
let jsonString2 = """
{
"name": "Tom",
"age": 25
}
"""
// ✅ 模拟网络返回的二进制 Data
if let jsonData = jsonString2.data(using: .utf8) {
do {
// ✅ JSONDecoder 解码 Data → Person 对象
let person = try JSONDecoder().decode(Person2.self, from: jsonData)
// ✅ 使用解析出的对象
print("名字:\(person.name)")
print("年龄:\(person.age)")
// ✅ 编码示例(Swift 对象 → JSON)
let encodedData = try JSONEncoder().encode(person)
if let jsonString = String(data: encodedData, encoding: .utf8) {
print("编码后的 JSON 字符串:\(jsonString)")
}
} catch {
print("解析或编码失败:\(error)")
}
}
解码和编码
我们来看一看解码内部,decode遵循Decodable协议,返回一个实例对象
Decodable和Encodable本身没有什么好说的,就是把对象编码成其他格式和把其他格式解码并初始化一个实例对象的能力,里面有方法encode和decode,重要的是其中的方法遵循的协议。Eocoder和Decoder。解码时,会生成一个实例对象,对象的参数必须初始化,所以使用init()。 下面是Encoder协议
swift
public protocol Encoder {
/// The path of coding keys taken to get to this point in encoding.
var codingPath: [any CodingKey] { get }
/// Any contextual information set by the user for encoding.
var userInfo: [CodingUserInfoKey : Any] { get }
/// Returns an encoding container appropriate for holding multiple values
/// keyed by the given key type.
///
/// You must use only one kind of top-level encoding container. This method
/// must not be called after a call to `unkeyedContainer()` or after
/// encoding a value through a call to `singleValueContainer()`
///
/// - parameter type: The key type to use for the container.
/// - returns: A new keyed encoding container.
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey
/// Returns an encoding container appropriate for holding multiple unkeyed
/// values.
///
/// You must use only one kind of top-level encoding container. This method
/// must not be called after a call to `container(keyedBy:)` or after
/// encoding a value through a call to `singleValueContainer()`
///
/// - returns: A new empty unkeyed container.
func unkeyedContainer() -> any UnkeyedEncodingContainer
/// Returns an encoding container appropriate for holding a single primitive
/// value.
///
/// You must use only one kind of top-level encoding container. This method
/// must not be called after a call to `unkeyedContainer()` or
/// `container(keyedBy:)`, or after encoding a value through a call to
/// `singleValueContainer()`
///
/// - returns: A new empty single value container.
func singleValueContainer() -> any SingleValueEncodingContainer
}
协议有三种方法,定义了三种容器,即按照编码的类型不同,放入不同的容器中,container适合键值对的数据,unkeyedContainer适合无键的数据,即数组,singleValueContainer适合单个的数据。键值对数据要遵守CodingKey协议,这是什么?
由源码可以知道,它从对象属性得到key,映射对象属性和最终外部数据之间的对应关系。
事情还没有完,得到容器类型后呢?看看第一种,
它遵循了一个协议,协议下面有编码方法encode,就得到一个想要的类型,

一个完整的编码步骤应该是这样:
swift
import Foundation
struct User: Encodable {
var fullName: String
var age: Int
var email: String
// 定义自定义 CodingKey
enum CodingKeys: String, CodingKey {
case name // fullName 映射到 "name"
case age
case email
}
func encode(to encoder: Encoder) throws {
// 获取 Keyed container
var container = encoder.container(keyedBy: CodingKeys.self)
// 手动映射属性到 key,并进行自定义逻辑
try container.encode(fullName, forKey: .name)
// 自定义处理,比如把年龄「加密」后再存
let encryptedAge = age + 100
try container.encode(encryptedAge, forKey: .age)
try container.encode(email, forKey: .email)//常规
}
}
你已经看到,我在编码时手动实现了一些东西,这是它的优势,虽然代码量大了 我可以修改:
- 1 属性名映射(外部 key 与内部属性不同)
swift
struct User: Encodable {
var fullName: String
enum CodingKeys: String, CodingKey {
case name // JSON 需要的 key
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fullName, forKey: .name)
}
}
- 加密或脱敏后再编码
比如你想对涉密字段加密
swift
try container.encode(phoneNumber.masked(), forKey: .phone)
- 只编码部分字段、合并字段后编码、给编码的字段加条件
swift
// 不编码某些属性(不写就不会被编码)
try container.encode(name, forKey: .name)
// 不调用 encode(password, forKey: .password),密码就不会被写入
swift
let fullName = "\(firstName) \(lastName)"
try container.encode(fullName, forKey: .fullName)
swift
if isPremiumUser {
try container.encode(premiumInfo, forKey: .premiumInfo)
}
只有会员才编码写入json
解码也类似:
swift
struct User: Decodable {
var fullName: String
var age: Int
var email: String
// 定义自定义 CodingKey
enum CodingKeys: String, CodingKey {
case name
case age
case email
}
// ✅ 自定义解码逻辑
init(from decoder: Decoder) throws {
// 获取 Keyed container
let container = try decoder.container(keyedBy: CodingKeys.self)
// 这里可以进行复杂映射或校验
// 例如:把 JSON 里的 name 映射到 fullName
self.fullName = try container.decode(String.self, forKey: .name)
// 自定义处理:从「加密」后的 age 解密
let encryptedAge = try container.decode(Int.self, forKey: .age)
self.age = encryptedAge - 100
// 常规解码 email
self.email = try container.decode(String.self, forKey: .email)
}
}