Encoding and Decoding Custom Types

Make your data types encodable and decodable for compatibility with external representations such as JSON.
使您的数据类型可编码和可解码,以便与外部表示(如JSON)兼容。

Overview

Many programming tasks involve sending data over a network connection, saving data to disk, or submitting data to APIs and services. These tasks often require data to be encoded and decoded to and from an intermediate format while the data is being transferred.

许多编程任务涉及通过网络连接发送数据,将数据保存到磁盘或将数据提交到API和服务。 这些任务通常要求在传输数据时将数据编码和解码为中间格式。

The Swift standard library defines a standardized approach to data encoding and decoding. You adopt this approach by implementing the Encodable and Decodable protocols on your custom types. Adopting these protocols lets implementations of the Encoder and Decoder protocols take your data and encode or decode it to and from an external representation such as JSON or property list. To support both encoding and decoding, declare conformance to Codable, which combines the Encodable and Decodable protocols. This process is known as making your types codable.

Swift标准库定义了数据编码和解码的标准化方法。 您可以通过在自定义类型上实现Encodable和Decodable协议来使用此方法。 遵守了这些协议,编码器和解码器协议的实现就可以接收您的数据,并将这些数据编码或解码为外部表示形式(如JSON或属性列表)。 为了支持编码和解码,需要遵守Codable协议,Codable结合了Encodable和Decodable协议。 这个过程被称为使您的类型可编码。

Encode and Decode Automatically

The simplest way to make a type codable is to declare its properties using types that are already Codable. These types include standard library types like String, Int, and Double; and Foundation types like DateData, and URL. Any type whose properties are codable automatically conforms to Codable just by declaring that conformance.

使类型可编码的最简单方法是使用已经可编码的类型声明其属性。 这些类型包括标准库类型,如String,Int和Double; 和基础类型,如日期,数据和URL。 如果一个类型的属性类型都遵守了Codable协议,那么要让这个类型可编码,只需要遵守Codable协议

Consider a Landmark structure that stores the name and founding year of a landmark:

考虑一个“Landmark”结构,它存储地标的名称和创始年份:

struct Landmark {
    var name: String
    var foundingYear: Int
}

Adding Codable to the inheritance list for Landmark triggers an automatic conformance that satisfies all of the protocol requirements from Encodable and Decodable:

将Codable添加到Landmark的继承列表会触发满足Encodable和Decodable的所有协议要求的自动一致性:
ps: 即只要遵守了Codable协议,相当于同时遵守了Encodeable 协议和Decodable协议

struct Landmark: Codable {
    var name: String
    var foundingYear: Int
    
    // Landmark now supports the Codable methods init(from:) and encode(to:), 
    // even though they aren't written as part of its declaration.
    Landmark现在支持Codable方法init(从:)和编码(到:),
    即使它们不是作为其声明的一部分编写的。
}

Adopting Codable on your own types enables you to serialize them to and from any of the built-in data formats, and any formats provided by custom encoders and decoders. For example, the Landmark structure can be encoded using both the PropertyListEncoder and JSONEncoder classes, even though Landmark itself contains no code to specifically handle property lists or JSON.

在您自己的类型上采用Codable使您可以将它们序列化为任何内置数据格式,以及自定义编码器和解码器提供的任何格式。 例如,Landmark结构可以使用PropertyListEncoder和JSONEncoder类进行编码,即使Landmark本身不包含专门处理属性列表或JSON的代码。

The same principle applies to custom types made up of other custom types that are codable. As long as all of its properties are Codable, any custom type can also be Codable.

同样的原则适用于由可编码的其他自定义类型组成的自定义类型。 只要它的所有属性都是Codable,任何自定义类型也可以是Codable。

The example below shows how automatic Codable conformance applies when a location property is added to the Landmark structure:

下面的示例显示了将位置属性添加到Landmark结构时如何应用自动Codable一致性:

struct Coordinate: Codable {
    var latitude: Double
    var longitude: Double
}

struct Landmark: Codable {
    // Double, String, and Int all conform to Codable.
    var name: String
    var foundingYear: Int
    
    // Adding a property of a custom Codable type maintains overall Codable conformance.
    var location: Coordinate
}

Built-in types such as Array, Dictionary, and Optional also conform to Codablewhenever they contain codable types. You can add an array of Coordinate instances to Landmark, and the entire structure will still satisfy Codable.

The example below shows how automatic conformance still applies when adding multiple properties using built-in codable types within Landmark:

内置类型(如Array,Dictionary和Optional)在包含可编码类型时也符合Codable。 您可以向Landmark添加一个Coordinate实例数组,整个结构仍将满足Codable。
下面的示例显示了在Landmark中使用内置可编码类型添加多个属性时,自动一致性如何仍然适用:

struct Landmark: Codable {
    var name: String
    var foundingYear: Int
    var location: Coordinate
    
    // Landmark is still codable after adding these properties.
    var vantagePoints: [Coordinate]
    var metadata: [String: String]
    var website: URL?
}

Encode or Decode Exclusively

In some cases, you may not need Codable's support for bidirectional encoding and decoding. For example, some apps only need to make calls to a remote network API and do not need to decode a response containing the same type. Declare conformance to Encodable if you only need to support the encoding of data. Conversely, declare conformance to Decodable if you only need to read data of a given type.

在某些情况下,您可能不需要Codable支持双向编码和解码。 例如,某些应用程序只需要调用远程网络API,而不需要解码包含相同类型的响应。 如果您只需要支持数据编码,则声明符合Encodable。 相反,如果您只需要读取给定类型的数据,则声明符合Decodable。

The examples below show alternative declarations of the Landmark structure that only encode or decode data:

以下示例显示了仅对数据进行编码或解码的Landmark结构的替代声明:

struct Landmark: Encodable {
    var name: String
    var foundingYear: Int
}
struct Landmark: Decodable {
    var name: String
    var foundingYear: Int
}

Choose Properties to Encode and Decode Using Coding Keys

Codable types can declare a special nested enumeration named CodingKeys that conforms to theCodingKeyprotocol. When this enumeration is present, its cases serve as the authoritative list of properties that must be included when instances of a codable type are encoded or decoded. The names of the enumeration cases should match the names you've given to the corresponding properties in your type.

Codable类型可以声明一个名为CodingKeys的特殊嵌套枚举,它符合CodingKey协议。 当存在该枚举时,
其成员用作在编码或解码可编码类型的实例时必须包括的属性的权威列表。(当一个codable类型的实例编码或者解码时,它的成员用作权威的属性列表必须被包含),枚举成员的名称应与您的类型中的相应属性指定的名称相匹配。

Omit properties from the CodingKeys enumeration if they won't be present when decoding instances, or if certain properties shouldn't be included in an encoded representation. A property omitted from CodingKeys needs a default value in order for its containing type to receive automatic conformance to Decodable or Codable.

如果在解码实例时它们不存在,或者如果某些属性不应包含在编码表示中,则忽略CodingKeys枚举中的属性。 CodingKeys中省略的属性需要一个默认值,以使其包含类型能够接收与Decodable或Codable的自动一致性。

If the keys used in your serialized data format don't match the property names from your data type, provide alternative keys by specifying String as the raw-value type for the CodingKeys enumeration. The string you use as a raw value for each enumeration case is the key name used during encoding and decoding. The association between the case name and its raw value lets you name your data structures according to the Swift API Design Guidelines rather than having to match the names, punctuation, and capitalization of the serialization format you're modeling.

如果序列化数据格式中使用的键与你的数据类型中的属性名称不匹配,请通过将String指定为CodingKeys枚举的原始值类型来提供备用键。 用作每个枚举情况的原始值的字符串是在编码和解码期间使用的键名。 成员名称与其原始值之间的关联使您可以根据Swift API设计指南命名数据结构,而不必匹配您正在建模的序列化格式的名称,标点符号和大小写。

The example below uses alternative keys for the name and foundingYear properties of the Landmark structure when encoding and decoding:

以下示例在编码和解码时使用替代键作为Landmark结构的name和foundingYear属性:

struct Landmark: Codable {
    var name: String
    var foundingYear: Int
    var location: Coordinate
    var vantagePoints: [Coordinate]
    
    enum CodingKeys: String, CodingKey {
        case name = "title"
        case foundingYear = "founding_date"
        
        case location
        case vantagePoints
    }
}

Encode and Decode Manually

If the structure of your Swift type differs from the structure of its encoded form, you can provide a custom implementation of Encodable and Decodable to define your own encoding and decoding logic.

如果你的Swift类型的结构与其编码形式的结构不同,则可以提供Encodable和Decodable的自定义实现来定义自己的编码和解码逻辑。

In the examples below, the Coordinate structure is expanded to support an elevation property that's nested inside of an additionalInfo container:

在下面的示例中,扩展了Coordinate结构以支持嵌套在additionalInfo容器内的提升属性:

struct Coordinate {
    var latitude: Double
    var longitude: Double
    var elevation: Double

    enum CodingKeys: String, CodingKey {
        case latitude
        case longitude
        case additionalInfo
    }
    
    enum AdditionalInfoKeys: String, CodingKey {
        case elevation
    }
}

Because the encoded form of the Coordinate type contains a second level of nested information, the type's adoption of the Encodable and Decodable protocols uses two enumerations that each list the complete set of coding keys used on a particular level.

因为Coordinate类型的编码形式包含第二级嵌套信息,所以类型采用Encodable和Decodable协议使用两个枚举,每个枚举列出在特定级别上使用的完整编码密钥集。

In the example below, the Coordinate structure is extended to conform to the Decodable protocol by implementing its required initializer, init(from:):

在下面的示例中,通过实现其所需的初始化程序init(from :),扩展了Coordinate结构以符合Decodable协议:

extension Coordinate: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        latitude = try values.decode(Double.self, forKey: .latitude)
        longitude = try values.decode(Double.self, forKey: .longitude)
        
        let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
        elevation = try additionalInfo.decode(Double.self, forKey: .elevation)
    }
}

The initializer populates a Coordinate instance by using methods on the Decoder instance it receives as a parameter. The Coordinate instance's two properties are initialized using the keyed container APIs provided by the Swift standard library.

初始化程序通过使用它作为参数接收的Decoder实例上的方法来填充Coordinate实例。 Coordinate实例的两个属性使用Swift标准库提供的键控容器API进行初始化。

The example below shows how the Coordinate structure can be extended to conform to the Encodable protocol by implementing its required method, encode(to:):

下面的示例显示了如何通过实现其所需的方法encode(to:)来扩展Coordinate结构以符合Encodable协议:

extension Coordinate: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(latitude, forKey: .latitude)
        try container.encode(longitude, forKey: .longitude)
        
        var additionalInfo = container.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
        try additionalInfo.encode(elevation, forKey: .elevation)
    }
}

This implementation of the encode(to:) method reverses the decoding operation from the previous example.

encode(to :)方法的这种实现颠倒了前一个例子的解码操作。

For more information about the container types used when customizing the encoding and decoding process, see KeyedEncodingContainerProtocol and UnkeyedEncodingContainer.

有关自定义编码和解码过程时使用的容器类型的更多信息,请参阅KeyedEncodingContainerProtocol和UnkeyedEncodingContainer。

扩展

Decodable

/// A type that can decode itself from an external representation.
一种可以从外部表示中解码自身的类型。

public protocol Decodable {

    /// Creates a new instance by decoding from the given decoder.
    /// 通过从给定解码器解码来创建新实例。
    /// This initializer throws an error if reading from the decoder fails, or
    /// if the data read is corrupted or otherwise invalid.
    /// 如果从解码器读取失败,或者读取的数据已损坏或无效,则此初始化程序将引发错误。
    /// - Parameter decoder: The decoder to read data from. 解码器从中读取数据。
    public init(from decoder: Decoder) throws
}

Encodable

/// A type that can encode itself to an external representation.
/// 一种可以将自身编码为外部表示的类型。
public protocol Encodable {

    /// Encodes this value into the given encoder.
    /// 将此值编码到给定的编码器中。
    /// If the value fails to encode anything, `encoder` will encode an empty
    /// keyed container in its place.
    /// 如果该值无法编码任何内容,`encoder`将在其位置编码一个空的键控容器。
    /// This function throws an error if any values are invalid for the given
    /// encoder's format.
    ///  如果任何值对于给定编码器的格式无效,则此函数将引发错误。
    /// - Parameter encoder: The encoder to write data to.
    public func encode(to encoder: Encoder) throws
}
    /// Returns the data stored in this decoder as represented in a container
    /// keyed by the given key type.
    /// 返回存储在此解码器中的数据,如由给定键类型键入的容器中所表示的。
    /// - parameter type: The key type to use for the container. 用于容器的密钥类型。
    /// - returns: A keyed decoding container view into this decoder.
    /// 密钥解码容器视图到此解码器。
    /// - throws: `DecodingError.typeMismatch` if the encountered stored value is
    ///   not a keyed container.
    ///  如果遇到的存储值不是键控容器,则为“DecodingError.typeMismatch”。
    public func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey

KeyedEncodingContainerProtocol

A type that provides a view into an encoder’s storage and is used to hold the encoded properties of an encodable type in a keyed manner.

一种类型,提供编码器存储的视图,用于以键的方式保存可编码类型的编码属性。

Overview

Encoders should provide types conforming to KeyedEncodingContainerProtocol for their format.

编码器应为其格式提供符合KeyedEncodingContainerProtocol的类型。

UnkeyedEncodingContainer

A type that provides a view into an encoder’s storage and is used to hold the encoded properties of an encodable type sequentially, without keys.

一种类型,提供编码器存储的视图,用于按顺序保存可编码类型的编码属性,无需键。

Overview

Encoders should provide types conforming to UnkeyedEncodingContainer for their format.

编码器应提供符合UnkeyedEncodingContainer格式的类型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343