Özellikler Swift 4'ün Kodlanabilir özelliğinden nasıl çıkarılır?


104

Swift 4'ün yeni Encodable/ Decodableprotokolleri JSON (de) serileştirmeyi oldukça keyifli hale getiriyor. Bununla birlikte, hangi özelliklerin kodlanması ve hangilerinin kodunun çözülmesi gerektiği konusunda ayrıntılı bir kontrole sahip olmanın bir yolunu henüz bulamadım.

Eşlik eden CodingKeysnumaralandırmadan mülkün hariç tutulmasının, mülkü işlemden tamamen hariç tuttuğunu fark ettim , ancak daha ayrıntılı denetime sahip olmanın bir yolu var mı?


Kodlamak istediğiniz bazı özelliklere sahip olduğunuz, ancak kodunu çözmek istediğiniz farklı özelliklere sahip olduğunuz bir durum olduğunu mu söylüyorsunuz? (yani, türünüzün geri dönüşlü olmamasını mı istiyorsunuz?) Çünkü sadece özelliği dışlamayı önemsiyorsanız, ona varsayılan bir değer vermek ve CodingKeysnumaralandırmanın dışında bırakmak yeterli olacaktır.
Itai Ferber

Her şeye rağmen, süreç üzerinde tam kontrol sağlamak için Codableprotokolün gereksinimlerini ( init(from:)ve encode(to:)) her zaman manuel olarak uygulayabilirsiniz.
Itai Ferber

Benim özel kullanım durumum, kod çözücüye çok fazla kontrol vermekten kaçınmaktır; bu, dahili özellik değerlerinin üzerine yazılmasıyla uzaktan JSON elde edilmesine yol açabilir. Aşağıdaki çözümler yeterlidir!
RamwiseMatt

1
Normalde ücretsiz olarak edinmeniz gereken tüm özellikleri yeniden uygulamak yerine, yalnızca özel durumları ve hariç tutulan anahtarları işlemeyi gerektiren bir cevap / yeni Swift özelliği görmek istiyorum.
pkamb

Yanıtlar:


183

Kodlama / kod çözme anahtarlarının listesi, adı verilen bir tür tarafından kontrol edilir CodingKeys( ssonuna dikkat edin ). Derleyici bunu sizin için sentezleyebilir, ancak bunu her zaman geçersiz kılabilir.

Diyelim ki mülkü hariç tutmak istediğiniz diyelim nicknamekodlama hem ve şifre çözme:

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

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

Asimetrik olmasını istiyorsanız (yani kodlayıp çözmemesini veya tam tersi), kendi uygulamalarınızı sağlamalısınız encode(with encoder: )ve init(from decoder: ):

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

    // Since fullName is a computed property, it's 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)
    }
}

17
Bunun nicknameçalışması için varsayılan bir değer vermeniz gerekiyor . Aksi takdirde, mülke atanabilecek değer yoktur init(from:).
Itai Ferber

1
@ItaiFerber Onu isteğe bağlı olarak değiştirdim, orijinal olarak Xcode'umda vardı
Kod Farklı

encodeAsimetrik örnekte sağlamanız gerektiğinden emin misiniz ? Hala standart davranış olduğu için buna ihtiyaç olduğunu düşünmedim. Sadece decodeasimetri geldiğinden beri.
Mark A. Donohoe

1
@MarqueIV Evet, mecbursunuz. Yana fullNamesaklı bir mülke eşlenemeyen, özel bir encoder ve dekoder sağlamalıdır.
Kod Farklı

2

Yapıdaki geniş bir özellik kümesinden birkaç özelliğin kodunu çözmemiz gerekirse, bunları isteğe bağlı özellikler olarak bildirin. Seçeneklerin sarmalanmaması için kod, CodingKey sıralaması altında çok sayıda anahtar yazmaktan daha azdır.

Hesaplanan örnek özellikleri ve hesaplanan tür özellikleri eklemek için uzantıların kullanılmasını tavsiye ederim. Kodlanabilir uyumlu özellikleri diğer mantıktan ayırır, dolayısıyla daha iyi okunabilirlik sağlar.


2

Bazı özellikleri kodlayıcıdan hariç tutmanın başka bir yolu, ayrı kodlama kabı kullanılabilir

struct Person: Codable {
    let firstName: String
    let lastName: String
    let excludedFromEncoder: String

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

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

        excludedFromEncoder = try anotherContainer(String.self, forKey: . excludedFromEncoder)
    }

    // it is not necessary to implement custom encoding
    // func encode(to encoder: Encoder) throws

    // let person = Person(firstName: "fname", lastName: "lname", excludedFromEncoder: "only for decoding")
    // let jsonData = try JSONEncoder().encode(person)
    // let jsonString = String(data: jsonData, encoding: .utf8)
    // jsonString --> {"firstName": "fname", "lastName": "lname"}

}

kod çözücü için aynı yaklaşım kullanılabilir


1

Hesaplanan özellikleri kullanabilirsiniz:

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

  var nick: String {
    get {
      nickname ?? ""
    }
  }

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

Bu benim için lazy varipucuydu - onu bir çalışma zamanı özelliğini etkili bir şekilde kullanmak onu Kodlanabilir'in dışında bıraktı.
ChrisH

0

Bu ise edebilir yapılabilir sonuçta çok olmak biter unSwifty ve hatta unJSONy . Sanırım nereden geldiğinizi anlıyorum, #ids kavramı HTML'de yaygın, ancak nadiren iyi bir şey (TM) olarak JSONgördüğüm dünyaya taşınıyor .

Özyinelemeli karmalar kullanarak yeniden yapılandırırsanız, yani yalnızca sırayla (bir veya birkaç) içeren bir dizi içeriyorsa , bazı Codableyapılar JSONdosyanızı tam olarak ayrıştırabilir . Bu şekilde ayrıştırıcı, ilk etapta ağınızı bir araya getirmenize yardımcı olur ve gerçekten ihtiyacınız varsa , yapının basit bir geçişi yoluyla yalnızca bazı geri bağlantılar sağlamanız gerekir . Bu, sizin ve veri yapınızın baştan sona yeniden çalışılmasını gerektirdiğinden, sadece sizin düşünmeniz için fikrin taslağını çıkarıyorum. Kabul edilebilir olduğunu düşünüyorsanız, lütfen yorumlarda bana söyleyin, o zaman daha fazla ayrıntıya girebilirim, ancak şartlara bağlı olarak ikisini de değiştirme özgürlüğünüz olmayabilir.recipeingredientsingredient_infoJSON


0

Protokolü ve uzantısını AssociatedObject ile birlikte görüntü (veya Kodlanabilir özelliğinden hariç tutulması gereken herhangi bir özellik) ayarlamak ve almak için kullandım.

Bununla kendi Kodlayıcı ve Kod Çözücümüzü uygulamak zorunda değiliz

İşte basitlik için ilgili kodu koruyan kod:

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
}

Şimdi, Image özelliğine ne zaman erişmek istersek, protokole onay veren nesne üzerinde kullanabiliriz (SCAttachmentModelProtocol)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.