コーディングキーの手動カスタマイズ
あなたの例では、Codable
すべてのプロパティもに準拠しているため、自動生成された準拠を取得していますCodable
。この準拠により、プロパティ名に単に対応するキータイプが自動的に作成されます。これは、単一のキー付きコンテナーへのエンコード/デコードを行うために使用されます。
ただし、この自動生成された適合の本当に優れた機能の1つは、プロトコルに準拠したenum
「CodingKeys
」と呼ばれるタイプにネストを定義する(またはtypealias
この名前でを使用する)と、CodingKey
Swiftはこれをキータイプとして自動的に使用します。したがって、これにより、プロパティのエンコード/デコードに使用するキーを簡単にカスタマイズできます。
つまり、これはあなたがただ言うことができるということです:
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
}
}
列挙型のケース名はプロパティ名と一致する必要があり、これらのケースの未加工の値は、エンコード先またはデコード元のキーと一致する必要があります(特に指定しない限り、String
列挙型の未加工の値はケース名と同じになります) )。したがって、zip
プロパティはキーを使用してエンコード/デコードされます"zip_code"
。
自動生成されたEncodable
/ Decodable
適合の正確なルールは、進化の提案(鉱山の強調)で詳しく説明されています。
自動に加えCodingKey
の要件合成
enums
、Encodable
およびDecodable
要件も自動的に特定の種類のために合成することができます。
Encodable
プロパティがすべて一致するタイプには、プロパティがケース名にマッピングEncodable
さString
れたCodingKey
列挙型マッピングが自動的に生成されます。同様にDecodable
、プロパティがすべてであるタイプの場合Decodable
(1)に落ちるタイプ- 手動で提供し、タイプCodingKey
enum
(名前CodingKeys
、直接、またはを介してtypealias
)、その場合1対1のマッピングEncodable
/ Decodable
名前によって特性が -の自動合成取得init(from:)
し、encode(to:)
それらの特性およびキーを使用して、適切なように
陥るタイプでもない(1)や(2)必要に応じてカスタムキーの種類を提供し、独自に提供する必要がありますinit(from:)
し、
encode(to:)
必要に応じて、
エンコーディングの例:
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"}
デコードの例:
// 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")
プロパティ名の自動snake_case
JSONキーcamelCase
Swift 4.1では、zip
プロパティの名前をに変更すると、およびの間で自動的にコーディングキーを変換するためにzipCode
、JSONEncoder
およびJSONDecoder
でキーのエンコード/デコード戦略を利用できます。camelCase
snake_case
エンコーディングの例:
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"}
デコードの例:
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")
ただし、この戦略について注意すべき重要な点の1つは、Swift API設計ガイドラインによれば、大文字と小文字を同じにする必要がある頭字語や頭文字で一部のプロパティ名をラウンドトリップできないことです(位置によって異なります)。 )。
たとえば、という名前のプロパティsomeURL
はキーsome_url
でエンコードされますが、デコードすると、に変換されsomeUrl
ます。
この問題を解決するには、手動で例えば、文字列デコーダ期待していること、であることをそのプロパティのコーディングキーを指定する必要がありますsomeUrl
(まだに変換されます。この場合、some_url
エンコーダによって):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(これは厳密にあなたの特定の質問に答えるものではありませんが、このQ&Aの標準的な性質を考えると、含める価値はあると思います)
カスタムの自動JSONキーマッピング
スウィフト4.1では、カスタムキーに戦略を符号化/復号化を利用することができますJSONEncoder
とJSONDecoder
、あなたがコーディングのキーをマップするカスタム機能を提供することができます。
提供する関数はを受け取ります[CodingKey]
。これは、エンコード/デコードの現在のポイントのコーディングパスを表します(ほとんどの場合、最後の要素、つまり現在のキーのみを考慮する必要があります)。関数は、CodingKey
この配列の最後のキーを置き換えるa を返します。
たとえばUpperCamelCase
、lowerCamelCase
プロパティ名のJSONキー:
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
}
}
}
これで、.convertToUpperCamelCase
キー戦略でエンコードできます。
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"}
そして.convertFromUpperCamelCase
重要な戦略でデコードします:
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
列挙型の下のケースステートメント。変更する1つのキーのみを一覧表示できますか?