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

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

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

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
}

person Suhit Patil    schedule 04.09.2018    source источник
comment
Просто let upvote: Status и let read: Status в struct Actions?   -  person Tj3n    schedule 04.09.2018
comment
Мне нужно свойство статуса внутри UpvoteStatus и ReadStatus, поэтому, если будут добавлены дополнительные свойства, я могу добавить туда.   -  person Suhit Patil    schedule 04.09.2018
comment
@ Tj3n с использованием подкласса - это один из вариантов достижения решения, но для этого необходимо изменить структуру на класс.   -  person Suhit Patil    schedule 04.09.2018
comment
Вы не включили его в вопрос, поэтому я не знал, и да, единственный известный мне способ удалить эту переменную - это изменить класс и использовать для него подкласс, если вы хотите придерживаться структуры, тогда у вас есть чтобы сохранить его, но при желании можете рассматривать оба типа протокола как один (то же самое, что и использование родительского класса в качестве типа)   -  person Tj3n    schedule 05.09.2018


Ответы (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
} 
person Tj3n    schedule 04.09.2018
comment
пока идем с опцией структуры. - person Suhit Patil; 06.09.2018

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

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

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
        }
    }
}
person Luca Angeletti    schedule 04.09.2018