コーディング キーの手動カスタマイズ
あなたの例では、Codable
すべてのプロパティもに準拠しているため、自動生成された準拠を得ていますCodable
。この適合性により、プロパティ名に単純に対応するキー タイプが自動的に作成されます。これは、単一のキー付きコンテナーとの間でエンコード/デコードするために使用されます。
ただし、この自動生成された準拠の非常に優れた機能の 1 つは、プロトコルに準拠するenum
" " と呼ばれるCodingKeys
(またはtypealias
この名前で a を使用する)ネストされた型を定義すると、Swift はこれをキーの型としてCodingKey
自動的に使用することです。したがって、これにより、プロパティがエンコード/デコードされるキーを簡単にカスタマイズできます。
つまり、これが何を意味するかというと、次のように言えます。
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
プロパティは key を使用してエンコード/デコードされます"zip_code"
。
自動生成Encodable
/Decodable
適合の正確なルールは、進化の提案(強調鉱山) で詳述されています。
の自動CodingKey
要件合成に
加えてenums
、特定のタイプのEncodable
&Decodable
要件も自動合成できます。
Encodable
プロパティがすべてに準拠している型Encodable
は、自動的に生成された、プロパティをケース名にマッピングする にString
裏打ちされた列挙型を取得します。CodingKey
同様にDecodable
、プロパティがすべてDecodable
(1) に該当する型 — および手動でCodingKey
enum
(名前付きCodingKeys
、直接、または a を介してtypealias
) を提供する型で、そのケースは名前によって 1 対 1 でEncodable
/Decodable
プロパティにマップされます —これらのプロパティとキーを使用して、必要に応じてinit(from:)
との自動合成を取得します。encode(to:)
(1) にも (2) にも該当しないタイプは、必要に応じてカスタム キー タイプを提供し、必要に応じて独自のinit(from:)
and
を提供する必要があり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 キー マッピング
Swift 4.1 では、 および でカスタム キーのエンコード/デコード戦略をJSONEncoder
利用してJSONDecoder
、コーディング キーをマップするカスタム関数を提供できます。
提供する関数は[CodingKey]
、エンコード/デコードの現在のポイントのコーディング パスを表す を受け取ります (ほとんどの場合、最後の要素、つまり現在のキーのみを考慮する必要があります)。この関数は、CodingKey
この配列の最後のキーを置き換える a を返します。
たとえば、プロパティ名のUpperCamelCase
JSON キーは次のとおりです。lowerCamelCase
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")