可解码和枚举值
Decodable & Enum Values
解码object时遇到枚举值不存在的情况应该如何处理?例如,在下面的示例中,我将如何处理 "horrible" 是评级类型的情况?如果存在 none 个值,是否可以设置默认值?
public struct Review: Decodable {
public var ratingType: RatingType?
enum CodingKeys: String, CodingKey {
case ratingType = "rating_type"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
ratingType = try container.decodeIfPresent(RatingType.self, forKey: .ratingType)
}
}
public enum RatingType: String, Codable {
case Good = "good"
case Bad = "bad"
}
decodeIfPresent
returns nil
如果键不存在,如果值无效则可能会抛出错误。通过使用 try?
,我们可以这样做:
ratingType =
(try? (container.decodeIfPresent(RatingType.self, forKey: .ratingType) ?? <some default value>)
) ?? <some default value>
如果实际使用收到的意外评级(针对客户端和服务器之间版本不匹配的防御性编码或其他),您可以尝试使用不同的枚举定义:
enum RatingType
{
case good
case bad
case other(String)
}
您失去了自动编码但获得了灵活性。
可能的实现:
import Foundation
struct Review
{
public var rating: RatingType?
enum CodingKeys: String, CodingKey {
case rating = "rating_type"
}
public init(rating: RatingType) {
self.rating = rating
}
}
extension Review: Decodable
{
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let label = try container.decodeIfPresent(String.self, forKey: .rating) {
rating = RatingType(label: label)
}
}
}
extension Review: Encodable
{
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if let rating = rating {
try container.encode(rating.label, forKey: .rating)
}
}
}
.
enum RatingType
{
case good
case bad
case other(String)
init(label: String) {
switch label {
case "good": self = .good
case "bad": self = .bad
default: self = .other(label)
}
}
var label: String {
switch self {
case .bad: return "bad"
case .good: return "good"
case let .other(label): return label
}
}
}
在现实生活中,您可能希望 RatingType
符合 Codable
/Decodable
,简化 coding/encoding。随意修改。
练习encoding/decoding的例子:
func encode_review(_ review: Review) throws -> String
{
let data = try JSONEncoder().encode(review)
return String(data: data, encoding: String.Encoding.utf8) ?? "/* ERROR */"
}
func decode_json(_ json: String) throws -> Review?
{
guard let data = json.data(using: String.Encoding.utf8) else { return nil }
let review = try JSONDecoder().decode(Review.self, from: data)
return review
}
print(try! encode_review(Review(rating: .good))) // {"rating_type":"good"}
print(try! encode_review(Review(rating: .other("horrible")))) // {"rating_type":"horrible"}
let good_review_json = """
{"rating_type":"good"}
"""
let great_review_json = """
{"rating_type":"great"}
"""
if let review = try! decode_json(good_review_json), let label = review.rating?.label {
print(label) // good
}
if let review = try! decode_json(great_review_json), let label = review.rating?.label {
print(label) // great
}
解码object时遇到枚举值不存在的情况应该如何处理?例如,在下面的示例中,我将如何处理 "horrible" 是评级类型的情况?如果存在 none 个值,是否可以设置默认值?
public struct Review: Decodable {
public var ratingType: RatingType?
enum CodingKeys: String, CodingKey {
case ratingType = "rating_type"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
ratingType = try container.decodeIfPresent(RatingType.self, forKey: .ratingType)
}
}
public enum RatingType: String, Codable {
case Good = "good"
case Bad = "bad"
}
decodeIfPresent
returns nil
如果键不存在,如果值无效则可能会抛出错误。通过使用 try?
,我们可以这样做:
ratingType =
(try? (container.decodeIfPresent(RatingType.self, forKey: .ratingType) ?? <some default value>)
) ?? <some default value>
如果实际使用收到的意外评级(针对客户端和服务器之间版本不匹配的防御性编码或其他),您可以尝试使用不同的枚举定义:
enum RatingType
{
case good
case bad
case other(String)
}
您失去了自动编码但获得了灵活性。
可能的实现:
import Foundation
struct Review
{
public var rating: RatingType?
enum CodingKeys: String, CodingKey {
case rating = "rating_type"
}
public init(rating: RatingType) {
self.rating = rating
}
}
extension Review: Decodable
{
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let label = try container.decodeIfPresent(String.self, forKey: .rating) {
rating = RatingType(label: label)
}
}
}
extension Review: Encodable
{
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if let rating = rating {
try container.encode(rating.label, forKey: .rating)
}
}
}
.
enum RatingType
{
case good
case bad
case other(String)
init(label: String) {
switch label {
case "good": self = .good
case "bad": self = .bad
default: self = .other(label)
}
}
var label: String {
switch self {
case .bad: return "bad"
case .good: return "good"
case let .other(label): return label
}
}
}
在现实生活中,您可能希望 RatingType
符合 Codable
/Decodable
,简化 coding/encoding。随意修改。
练习encoding/decoding的例子:
func encode_review(_ review: Review) throws -> String
{
let data = try JSONEncoder().encode(review)
return String(data: data, encoding: String.Encoding.utf8) ?? "/* ERROR */"
}
func decode_json(_ json: String) throws -> Review?
{
guard let data = json.data(using: String.Encoding.utf8) else { return nil }
let review = try JSONDecoder().decode(Review.self, from: data)
return review
}
print(try! encode_review(Review(rating: .good))) // {"rating_type":"good"}
print(try! encode_review(Review(rating: .other("horrible")))) // {"rating_type":"horrible"}
let good_review_json = """
{"rating_type":"good"}
"""
let great_review_json = """
{"rating_type":"great"}
"""
if let review = try! decode_json(good_review_json), let label = review.rating?.label {
print(label) // good
}
if let review = try! decode_json(great_review_json), let label = review.rating?.label {
print(label) // great
}