【问题标题】:Decodable & Enum Values可解码和枚举值
【发布时间】:2018-01-09 16:49:18
【问题描述】:

解码对象时枚举值不存在的情况应该如何处理?例如,我将如何处理下面示例中评级类型为“可怕”的情况?如果不存在任何值,有没有办法设置默认值?

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"
}

【问题讨论】:

  • 如果值是“可怕的”,那么它会抛出一个错误。我想你能听懂。

标签: swift decodable


【解决方案1】:

decodeIfPresent 如果键不存在,则返回nil,如果值无效,它可能会抛出错误。通过使用try?,我们可以这样做:

ratingType = 
    (try? (container.decodeIfPresent(RatingType.self, forKey: .ratingType) ?? <some default value>)
    ) ?? <some default value>

【讨论】:

  • 可以在不使用解码器方法覆盖所需初始化的情况下进行解码。有没有办法告诉解码器它将使用“尝试”?而不是 decodeIfPresent 正在做的“尝试”。在我的情况下,我正在处理具有许多属性的结构,使用解码器和 CodingKeys 实现所需的 init 并将每个值映射到每个属性是非常乏味的。那么有没有办法告诉 JSONDecoder 使用 try 呢?而不是在从容器解码时尝试?
【解决方案2】:

如果应该实际使用收到的意外评级(针对客户端和服务器之间的版本不匹配的防御性编码或其他),您可以尝试使用不同的枚举定义:

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,从而简化编码/编码。随意修改。

执行编码/解码的示例:

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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-27
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2019-01-06
    • 1970-01-01
    • 2021-09-11
    • 2012-04-01
    相关资源
    最近更新 更多