Как использовать Decodable Protocol для создания общего класса для одинаковых свойств Json с разными ключами

{
    "actions" : {
        "upvote" : {
            "delete" : true,
            "read" : true,
            "create" : true,
            "update": true
        },
        "read" : {
            "delete" : true,
            "update" : true,
            "read" : true,
            "create" : true
        }
    }
}

У меня есть этот ответ Json, поступающий с сервера, и ниже приведены структурные модели, созданные с использованием декодируемого протокола.

struct Actions: Decodable {
    let upvote: UpvoteStatus
    let read: ReadStatus

    enum CodingKeys: String, CodingKey {
        case upvote
        case read
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.upvote = try container.decode(UpvoteStatus.self, forKey: .upvote) {
        self.read = try container.decode(ReadStatus.self, forKey: .read)
    }
}

struct UpvoteStatus: Decodable {
    let delete: Bool
    let update: Bool
    let read: Bool
    let create: Bool
}

struct ReadStatus: Decodable {
    var delete: Bool
    var update: Bool
    var read: Bool
    var create: Bool
}

Это прекрасно работает, но создает много дублирующегося кода, поскольку структуры UpvoteStatus и ReadStatus имеют схожие свойства, а JSON с сервера аналогичен, за исключением разных ключей.

Можно ли каким-либо образом создать общую структуру статуса, добавить свойство статуса в классы ReadStatus и UpvoteStatus?

struct Status: Decodable {
    let delete: Bool
    let update: Bool
    let read: Bool
    let create: Bool
} 

Теперь я хочу иметь что-то вроде ниже, чтобы я мог удалить дубликат кода.

struct UpvoteStatus: Decodable {
    let status: Status
}

struct ReadStatus: Decodable {
    let status: Status
}

2 ответа

Решение

Возможно, это то, что вам нужно, может быть, вы слишком усердно об этом думаете:

struct Actions: Decodable {
    let upvote: Status
    let read: Status

    enum CodingKeys: String, CodingKey {
        case upvote
        case read
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.upvote = try container.decode(Status.self, forKey: .upvote) {
        self.read = try container.decode(Status.self, forKey: .read)
    }
}

struct Status: Decodable {
    let delete: Bool
    let update: Bool
    let read: Bool
    let create: Bool
} 

Нет необходимости писать собственный инициализатор и явно определять ключи кодирования.

Вы можете просто написать

struct Response: Codable {

    let actions: Actions

    struct Actions : Codable {
        let upvote: Element
        let read: Element

        struct Element: Codable {
            let delete: Bool
            let read: Bool
            let create: Bool
            let update: Bool
        }
    }
}
Другие вопросы по тегам