Nowe protokoły Encodable
/ w Swift 4 Decodable
sprawiają, że serializacja JSON (de) jest całkiem przyjemna. Jednak nie znalazłem jeszcze sposobu na precyzyjną kontrolę nad tym, które właściwości powinny być zakodowane, a które zdekodowane.
Zauważyłem, że wykluczenie właściwości z towarzyszącego CodingKeys
wyliczenia całkowicie wyklucza właściwość z procesu, ale czy istnieje sposób na bardziej szczegółową kontrolę?
CodingKeys
wyliczeniem.Codable
protokołu (init(from:)
iencode(to:)
) ręcznie, aby uzyskać pełną kontrolę nad procesem.Odpowiedzi:
Lista kluczy do kodowania / dekodowania jest kontrolowana przez typ o nazwie
CodingKeys
(zwróć uwagęs
na koniec). Kompilator może zsyntetyzować to za Ciebie, ale zawsze może to zastąpić.Załóżmy, że chcesz wykluczyć właściwość
nickname
zarówno z kodowania, jak i dekodowania:struct Person: Codable { var firstName: String var lastName: String var nickname: String? private enum CodingKeys: String, CodingKey { case firstName, lastName } }
Jeśli chcesz, aby był asymetryczny (tj. Koduje, ale nie dekoduje lub odwrotnie), musisz dostarczyć własne implementacje
encode(with encoder: )
iinit(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) } }
źródło
nickname
to zadziałało, musisz podać wartość domyślną. W przeciwnym razie nie ma wartości, którą można przypisać do właściwościinit(from:)
.encode
w asymetrycznym przykładzie? Ponieważ jest to nadal standardowe zachowanie, nie sądziłem, że jest potrzebne. Tylkodecode
dlatego, że stąd bierze się asymetria.fullName
nie można zamapować na przechowywaną właściwość, należy podać niestandardowy koder i dekoder.CodingKeys
. Więcvar nickname: String { get { "name" } }
powinno wystarczyć.Jeśli musimy wykluczyć dekodowanie kilku właściwości z dużego zestawu właściwości w strukturze, zadeklaruj je jako właściwości opcjonalne. Kod do rozpakowywania opcji jest mniejszy niż pisanie wielu kluczy w wyliczeniu CodingKey.
Zalecałbym używanie rozszerzeń do dodawania obliczonych właściwości instancji i obliczonych właściwości typu. Oddziela kodowalne, zgodne właściwości od innej logiki, dzięki czemu zapewnia lepszą czytelność.
źródło
Innym sposobem na wykluczenie niektórych właściwości z kodera może być użycie oddzielnego kontenera kodowania
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"} }
to samo podejście można zastosować do dekodera
źródło
Możesz użyć obliczonych właściwości:
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 } }
źródło
lazy var
efektywne użycie właściwości środowiska wykonawczego wykluczyło ją z Codable.Chociaż można to zrobić, ostatecznie okazuje się, że jest to bardzo nierozsądne, a nawet nieskomplikowane . Myślę, że widzę, skąd pochodzisz, pojęcie
#id
s jest powszechne w HTML, ale rzadko przenoszone jest do świata,JSON
który uważam za dobrą rzecz (TM).Niektóre
Codable
struktury będą w stanieJSON
poprawnie przeanalizować plik, jeśli zmienisz jego strukturę za pomocą rekurencyjnych skrótów, tj. Jeśli Twojarecipe
po prostu zawiera tablicę,ingredients
która z kolei zawiera (jedną lub kilka)ingredient_info
. W ten sposób parser pomoże ci najpierw zszyć twoją sieć, a jeśli naprawdę ich potrzebujesz, wystarczy zapewnić kilka linków zwrotnych poprzez proste przejście przez strukturę . Ponieważ wymaga to gruntownej zmiany struktury twoichJSON
i twoich danych, szkicuję tylko pomysł, abyś o tym pomyślał. Jeśli uznasz to za dopuszczalne, poinformuj mnie o tym w komentarzach, wtedy mógłbym to bardziej rozwinąć, ale w zależności od okoliczności możesz nie mieć możliwości zmiany któregokolwiek z nich.źródło
Użyłem protokołu i jego rozszerzenia wraz z AssociatedObject, aby ustawić i pobrać właściwość obrazu (lub dowolnej właściwości, która musi zostać wykluczona z Codable).
Dzięki temu nie musimy wdrażać własnego kodera i dekodera
Oto kod, zachowując odpowiedni kod dla uproszczenia:
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 }
Teraz, ilekroć chcemy uzyskać dostęp do właściwości Image, możemy użyć na obiekcie potwierdzającym protokół (SCAttachmentModelProtocol)
źródło