Подтвердить что ты не робот

Как исключить свойства из Swift 4 Codable

Swfit 4 новых протокола Encodable/Decodable делают сериализацию JSON (de) весьма приятной. Тем не менее, я еще не нашел способ иметь мелкозернистый контроль над тем, какие свойства должны быть закодированы и которые должны быть декодированы.

Я заметил, что исключение свойства из сопроводительного перечисления CodingKeys исключает свойство из процесса в целом, но есть ли способ иметь более мелкозернистый контроль?

4b9b3361

Ответ 1

Список ключей для кодирования/декодирования управляется типом, называемым CodingKeys (обратите внимание на s в конце). Компилятор может синтезировать это для вас, но всегда может переопределить это.

Допустим, вы хотите исключить свойство nickname как из кодировки , так и из декодирования:

struct Person: Codable {
    var firstName: String
    var lastName: String
    var nickname: String?

    private enum CodingKeys: String, CodingKey {
        case firstName, lastName
    }
}

Если вы хотите, чтобы он был асимметричным (то есть кодировать, но не декодировать или наоборот), вы должны предоставить свои собственные реализации encode(with encoder: ) и init(from decoder: ):

struct Person: Codable {
    var firstName: String
    var lastName: String

    // Since fullName is a computed property, it excluded by default
    var fullName: String {
        return firstName + " " + lastName
    }

    private enum CodingKeys: String, CodingKey {
        case firstName
        case lastName
        case fullName
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        firstName = try container.decode(String.self, forKey: .firstName)
        lastName = try container.decode(String.self, forKey: .lastName)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(firstName, forKey: .firstName)
        try container.encode(lastName, forKey: .lastName)
        try container.encode(fullName, forKey: .fullName)
    }
}

Ответ 2

Когда вы указываете class или struct как соответствующие Codable, не делая ничего лишнего, вы полагаетесь на компилятор, синтезирующий CodingKeys, а также два протокола, которые составляют Codable: Decodable и Encodable. Это означает, что компилятор добавляет не только ключи кодирования enum для всех свойств, но и реализации функций init(from:) и encode(to:) с помощью этих клавиш.

Если вы хотите ничего, кроме этого поведения по умолчанию, вы должны реализовать части, которые хотите настроить самостоятельно (в вашем конкретном случае вам нужно только ввести ключи кодирования, чтобы указать только те, которые вы хотите включить):

class SomeClass: Codable {

    var id: UUID?
    var excludedBool: Bool? // knows what it did!

    // Needed if we ever want to init one of these without init(from:)...
    init() {}

    // Our own custom CodingKeys implementation (id only, excludedBool is excluded
    private enum CodingKeys: String, CodingKey { case id }

    // Decodable requirement
    required init(from decoder: Decoder) throws {

        // We only encoded id, so we only decode id
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(UUID.self, forKey: .id)

        // We do our own thing with excludedBool if needed

    }

    // Encodable requirement
    func encode(to encoder: Encoder) throws {

        // We only encode id... excludedBool knows what it did....
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)

    }

}

Ответ 3

Если нам нужно исключить декодирование пары свойств из большого набора свойств в структуре, объявите их как необязательные свойства. Код для распаковки опций меньше, чем написание большого количества ключей в перечислении CodingKey.

Я бы порекомендовал использовать расширения для добавления свойств вычисляемого экземпляра и свойств вычисляемого типа. Он отделяет кодируемые удобные свойства от другой логики и, следовательно, обеспечивает лучшую читаемость.

Ответ 4

Хотя это может быть сделано, в конечном итоге он становится очень неуверенным и даже не очень приятным. Я думаю, я понимаю, откуда вы, концепция #id распространена в HTML, но редко переносится в мир JSON который я считаю хорошей вещью (TM).

Некоторая Codable Структура сможет разобрать ваш JSON - файл просто отлично, если вы реструктурировать его с помощью рекурсивных хэш, то есть, если ваш recipe содержит только массив ingredients, которые, в свою очередь, содержит (один или несколько) ingredient_info. Таким образом, синтаксический анализатор в первую очередь поможет вам объединить вашу сеть, и вам нужно будет лишь предоставить несколько обратных ссылок через простой обход структуры, если они вам действительно нужны. Поскольку это требует тщательной доработки вашего JSON и вашей структуры данных, я лишь обрисовываю вам идею об этом подумать. Если вы сочтете это приемлемым, пожалуйста, сообщите мне в комментариях, тогда я мог бы уточнить это дальше, но в зависимости от обстоятельств вы не можете изменить ни один из них.

Ответ 5

Я использовал протокол и его расширение вместе с AssociatedObject для установки и получения свойства изображения (или любого свойства, которое необходимо исключить из Codable).

При этом нам не нужно реализовывать собственные кодеры и декодеры

Вот код, сохраняющий соответствующий код для простоты:

protocol SCAttachmentModelProtocol{
    var image:UIImage? {get set}
    var anotherProperty:Int {get set}
}
extension SCAttachmentModelProtocol where Self: SCAttachmentUploadRequestModel{
    var image:UIImage? {
        set{
            //Use associated object property to set it
        }
        get{
            //Use associated object property to get it
        }
    }
}
class SCAttachmentUploadRequestModel : SCAttachmentModelProtocol, Codable{
    var anotherProperty:Int
}

Теперь, когда мы хотим получить доступ к свойству Image, мы можем использовать объект, подтверждающий протокол (SCAttachmentModelProtocol)