Ręczne dostosowywanie kluczy kodowania
W swoim przykładzie otrzymujesz automatycznie wygenerowaną zgodność, z którą Codable
są zgodne również wszystkie twoje właściwości Codable
. Ta zgodność automatycznie tworzy typ klucza, który po prostu odpowiada nazwom właściwości - który jest następnie używany do kodowania do / dekodowania z kontenera z pojedynczym kluczem.
Jednak jeden naprawdę fajną cechą tej automatycznie generowanej zgodności jest to, że jeśli zdefiniujesz zagnieżdżony enum
w swoim typie o nazwie " CodingKeys
" (lub użyjesz a typealias
z tą nazwą), który jest zgodny z CodingKey
protokołem - Swift automatycznie użyje tego jako typu klucza. Dzięki temu możesz łatwo dostosować klucze, za pomocą których twoje właściwości są kodowane / dekodowane.
Oznacza to, że możesz po prostu powiedzieć:
struct Address : Codable {
var street: String
var zip: String
var city: String
var state: String
private enum CodingKeys : String, CodingKey {
case street, zip = "zip_code", city, state
}
}
Nazwy przypadków wyliczenia muszą pasować do nazw właściwości, a surowe wartości tych przypadków muszą być zgodne z kluczami, do których kodujesz / dekodujesz (o ile nie określono inaczej, surowe wartości String
wyliczenia będą takie same jak nazwy przypadków ). Dlatego zip
właściwość zostanie teraz zakodowana / zdekodowana przy użyciu klucza "zip_code"
.
Dokładne zasady generowania automatycznego Encodable
/ Decodable
zgodności są szczegółowo opisane w propozycji ewolucji (moje podkreślenie):
Oprócz automatycznej CodingKey
syntezy wymagań dla
enums
, wymagania Encodable
& Decodable
mogą być również automatycznie syntetyzowane dla niektórych typów:
Typy zgodne z Encodable
wszystkimi właściwościami są Encodable
generowane automatycznieString
-backed CodingKey
właściwości mapowania enum do nazw sprawy. Podobnie dla Decodable
typów, których wszystkie właściwości sąDecodable
Typy należące do (1) - i typy, które ręcznie zapewniają CodingKey
enum
(nazwane CodingKeys
, bezpośrednio lub przez a typealias
), których przypadki mapują 1-do-1 na Encodable
/Decodable
właściwości według nazwy - uzyskują automatyczną syntezę init(from:)
i, encode(to:)
jeśli to konieczne, przy użyciu tych właściwości i kluczy
Typy, które nie należą do ani (1), ani (2) będą musiały w razie potrzeby podać niestandardowy typ klucza i zapewnić własne init(from:)
i
encode(to:)
, w razie potrzeby
Przykładowe kodowanie:
import Foundation
let address = Address(street: "Apple Bay Street", zip: "94608",
city: "Emeryville", state: "California")
do {
let encoded = try JSONEncoder().encode(address)
print(String(decoding: encoded, as: UTF8.self))
} catch {
print(error)
}
//{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
Przykładowe dekodowanie:
// using the """ multi-line string literal here, as introduced in SE-0168,
// to avoid escaping the quotation marks
let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""
do {
let decoded = try JSONDecoder().decode(Address.self, from: Data(jsonString.utf8))
print(decoded)
} catch {
print(error)
}
// Address(street: "Apple Bay Street", zip: "94608",
// city: "Emeryville", state: "California")
Automatyczny snake_case
klucze JSON dla camelCase
nazw właściwości
W Swift 4.1, jeśli zmienisz nazwę swojej zip
właściwości na zipCode
, możesz skorzystać ze strategii kodowania / dekodowania klucza na JSONEncoder
iJSONDecoder
w celu automatycznej konwersji kluczy kodowania między camelCase
a snake_case
.
Przykładowe kodowanie:
import Foundation
struct Address : Codable {
var street: String
var zipCode: String
var city: String
var state: String
}
let address = Address(street: "Apple Bay Street", zipCode: "94608",
city: "Emeryville", state: "California")
do {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let encoded = try encoder.encode(address)
print(String(decoding: encoded, as: UTF8.self))
} catch {
print(error)
}
//{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
Przykładowe dekodowanie:
let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decoded = try decoder.decode(Address.self, from: Data(jsonString.utf8))
print(decoded)
} catch {
print(error)
}
// Address(street: "Apple Bay Street", zipCode: "94608",
// city: "Emeryville", state: "California")
Jedną ważną rzeczą, na którą należy zwrócić uwagę na temat tej strategii, jest to, że nie będzie ona w stanie przelać w obie strony niektórych nazw właściwości za pomocą akronimów lub inicjałów, które zgodnie z wytycznymi projektowymi Swift API powinny być jednolicie duże lub małe (w zależności od pozycji ).
Na przykład właściwość o nazwie someURL
zostanie zakodowana za pomocą klucza some_url
, ale podczas dekodowania zostanie przekształcona na someUrl
.
Aby to naprawić, musisz ręcznie określić klucz kodowania dla tej właściwości jako ciąg, którego dekoder oczekuje, np. someUrl
W tym przypadku (który nadal będzie przekształcany some_url
przez koder):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(To nie jest dokładną odpowiedzią na twoje konkretne pytanie, ale biorąc pod uwagę kanoniczny charakter tych pytań i odpowiedzi, uważam, że warto je uwzględnić)
Niestandardowe automatyczne mapowanie kluczy JSON
W Swift 4.1 możesz skorzystać z niestandardowych strategii kodowania / dekodowania kluczy włączonych JSONEncoder
i JSONDecoder
, co pozwala na zapewnienie niestandardowej funkcji mapowania kluczy kodowania.
Podana funkcja przyjmuje znak a [CodingKey]
, który reprezentuje ścieżkę kodowania dla bieżącego punktu w kodowaniu / dekodowaniu (w większości przypadków wystarczy wziąć pod uwagę tylko ostatni element, czyli bieżący klucz). Funkcja zwraca wartość CodingKey
, która zastąpi ostatni klucz w tej tablicy.
Na przykład UpperCamelCase
klucze JSON dla lowerCamelCase
nazw właściwości:
import Foundation
// wrapper to allow us to substitute our mapped string keys.
struct AnyCodingKey : CodingKey {
var stringValue: String
var intValue: Int?
init(_ base: CodingKey) {
self.init(stringValue: base.stringValue, intValue: base.intValue)
}
init(stringValue: String) {
self.stringValue = stringValue
}
init(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}
init(stringValue: String, intValue: Int?) {
self.stringValue = stringValue
self.intValue = intValue
}
}
extension JSONEncoder.KeyEncodingStrategy {
static var convertToUpperCamelCase: JSONEncoder.KeyEncodingStrategy {
return .custom { codingKeys in
var key = AnyCodingKey(codingKeys.last!)
// uppercase first letter
if let firstChar = key.stringValue.first {
let i = key.stringValue.startIndex
key.stringValue.replaceSubrange(
i ... i, with: String(firstChar).uppercased()
)
}
return key
}
}
}
extension JSONDecoder.KeyDecodingStrategy {
static var convertFromUpperCamelCase: JSONDecoder.KeyDecodingStrategy {
return .custom { codingKeys in
var key = AnyCodingKey(codingKeys.last!)
// lowercase first letter
if let firstChar = key.stringValue.first {
let i = key.stringValue.startIndex
key.stringValue.replaceSubrange(
i ... i, with: String(firstChar).lowercased()
)
}
return key
}
}
}
Możesz teraz kodować za pomocą .convertToUpperCamelCase
strategii klucza:
let address = Address(street: "Apple Bay Street", zipCode: "94608",
city: "Emeryville", state: "California")
do {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToUpperCamelCase
let encoded = try encoder.encode(address)
print(String(decoding: encoded, as: UTF8.self))
} catch {
print(error)
}
//{"Street":"Apple Bay Street","City":"Emeryville","State":"California","ZipCode":"94608"}
i dekoduj z .convertFromUpperCamelCase
kluczową strategią:
let jsonString = """
{"Street":"Apple Bay Street","City":"Emeryville","State":"California","ZipCode":"94608"}
"""
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromUpperCamelCase
let decoded = try decoder.decode(Address.self, from: Data(jsonString.utf8))
print(decoded)
} catch {
print(error)
}
// Address(street: "Apple Bay Street", zipCode: "94608",
// city: "Emeryville", state: "California")
CodingKeys
wyliczeniem; czy mogę wymienić tylko jeden klucz, który zmieniam?"""
jest literałem wielowierszowym :)"
s: DCodable
innego)Address
niepotrzebnie wiąże się z dekodowaniem obiektu JSON, który zaczyna się w określonym miejscu na grafie obiektu nadrzędnego. Byłoby znacznie przyjemniej wyodrębnić początkową ścieżkę klucza do samego dekodera - oto zgrubna implementacja hakerska .Dzięki Swift 4.2, zgodnie z własnymi potrzebami, możesz użyć jednej z 3 poniższych strategii, aby dopasować nazwy właściwości niestandardowych obiektów modelu do kluczy JSON.
# 1. Korzystanie z niestandardowych kluczy kodowania
Kiedy deklarujesz strukturę, która jest zgodna z
Codable
(Decodable
iEncodable
protokołami) z następującą implementacją ...... kompilator automatycznie generuje zagnieżdżone wyliczenie, które jest zgodne z
CodingKey
protokołem.W związku z tym, jeśli klucze używane w serializowanym formacie danych nie są zgodne z nazwami właściwości z typu danych, można ręcznie zaimplementować to wyliczenie i ustawić odpowiednie
rawValue
dla wymaganych przypadków.Poniższy przykład pokazuje, jak to zrobić:
Kodowanie (zastępując
zip
właściwość kluczem JSON „kod_zip”):Dekodowanie (zastępowanie klucza JSON „kod_zip”
zip
właściwością):# 2. Używanie etui węża do strategii kodowania kluczy w przypadku wielbłądów
Jeśli twój JSON ma klucze w kształcie węża i chcesz je przekonwertować na właściwości w obudowie wielbłąda dla obiektu modelu, możesz ustawić właściwości
JSONEncoder
'skeyEncodingStrategy
iJSONDecoder
' skeyDecodingStrategy
na.convertToSnakeCase
.Poniższy przykład pokazuje, jak to zrobić:
Kodowanie (konwersja właściwości w obudowie wielbłąda na klucze JSON w obudowie węża):
Dekodowanie (konwersja kluczy JSON w obudowie węża na właściwości w obudowie wielbłąda):
# 3. Korzystanie z niestandardowych strategii kodowania kluczy
W razie potrzeby
JSONEncoder
iJSONDecoder
pozwala ustawić niestandardową strategię mapowania kluczy kodowania za pomocąJSONEncoder.KeyEncodingStrategy.custom(_:)
iJSONDecoder.KeyDecodingStrategy.custom(_:)
.Poniższy przykład pokazuje, jak je wdrożyć:
Kodowanie (konwertowanie właściwości pierwszej litery małej litery na klucze JSON pierwszej litery małej litery):
Dekodowanie (konwersja kluczy JSON z wielkimi literami do właściwości pierwszej litery małej litery):
Źródła:
źródło
To, co zrobiłem, to stworzenie własnej struktury, tak jak to, co otrzymujesz z JSON w odniesieniu do jego typów danych.
Takie jak to:
Następnie musisz utworzyć rozszerzenie tego samego
struct
rozszerzeniadecodable
i tejenum
samej struktury zCodingKey
a następnie musisz zainicjalizować dekoder używając tego wyliczenia z jego kluczami i typami danych (klucze będą pochodzić z wyliczenia i typy danych będą przychodzić lub mówić odwołuje się do samej struktury)Musisz tutaj zmienić każdy klucz i typ danych zgodnie ze swoimi potrzebami i używać go z dekoderem.
źródło
Używając CodingKey , możesz używać niestandardowych kluczy w protokole kodowalnym lub dekodowalnym.
źródło