[解決済み] Swift 4のDecodableプロトコルでカスタムキーを使うには?
質問
Swift 4 では、JSON のネイティブなエンコードとデコードのサポートが導入されました。
Decodable
プロトコルによるネイティブな JSON エンコードとデコードのサポートを導入しました。この場合、カスタムキーをどのように使用すればよいですか?
例えば、構造体である
struct Address:Codable {
var street:String
var zip:String
var city:String
var state:String
}
これをJSONにエンコードすればいいんだ。
let address = Address(street: "Apple Bay Street", zip: "94608", city: "Emeryville", state: "California")
if let encoded = try? encoder.encode(address) {
if let json = String(data: encoded, encoding: .utf8) {
// Print JSON String
print(json)
// JSON string is
{ "state":"California",
"street":"Apple Bay Street",
"zip":"94608",
"city":"Emeryville"
}
}
}
これをエンコードしてオブジェクトに戻せばいい。
let newAddress: Address = try decoder.decode(Address.self, from: encoded)
しかし、もし私がjsonオブジェクトを
{
"state":"California",
"street":"Apple Bay Street",
"zip_code":"94608",
"city":"Emeryville"
}
のデコーダーにどのように伝えるのでしょうか?
Address
その
zip_code
は
zip
? 私は、あなたが新しい
CodingKey
プロトコルを使用していると思いますが、これをどのように使用するのかがわかりません。
どのように解決するのですか?
コーディング キーを手動でカスタマイズする
あなたの例では、自動生成された適合性のある
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
enum マッピング プロパティを大文字と小文字にマッピングします。同様に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")
この戦略について注意すべき重要なことは、頭字語や頭文字を含む一部のプロパティ名をラウンドトリップできないことです。 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
を返し、それがこの配列の最後のキーを置き換えます。
例えば
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")
関連
-
[解決済み] cURLでJSONデータをPOSTするにはどうすればよいですか?
-
[解決済み] SwiftからObjective-Cのコードを呼び出すにはどうしたらいいですか?
-
[解決済み] Swiftでindexとelementでループを反復させる方法
-
[解決済み] Swift カスタムオブジェクトの配列をプロパティ値でソートする方法
-
[解決済み] 純粋な」Swift で弱いプロトコル参照を作るには (@objc なしで) どうしたらいいですか?
-
[解決済み] JSONオブジェクトに末尾のカンマを使用することは可能ですか?
-
[解決済み】Swift 4でStringのsubstringを使うには?'substring(to:)'は非推奨です。部分的な範囲から'演算子を持つ文字列スライス添字を使用してください。
-
[解決済み] どのようにjqを使用して2つのファイルから2つのJSONオブジェクトをマージするには?
-
[解決済み] SwiftでJSON辞書の型を持つプロパティをデコードする方法 [45] デコード可能なプロトコル
-
[解決済み] Swift 4 の JSONDecoder で、見つからないキーは、オプションのプロパティである必要はなく、デフォルト値を使うことができますか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] TTLファイルフォーマット - これが何なのかさっぱり分からない
-
[解決済み] REST API - "Accept: application/json" HTTP ヘッダーを使用する
-
[解決済み] JSONの二重引用符をエスケープする方法
-
[解決済み] Node.jsでJSONオブジェクトの内容をログに記録する方法は?
-
[解決済み] jqを使用してjsonドキュメントの単一の値を更新するにはどうすればよいですか?
-
[解決済み] jsonパラメータを含むCurl GETリクエスト
-
[解決済み] VSCodeでlaunch.jsonに環境変数を追加する方法
-
[解決済み] CSV/XLSをJSONに変換する?[クローズド]
-
[解決済み] fs.writeFileSyncによるJSONオブジェクトのJSONファイルへの書き込み
-
[解決済み] PostgresでJSONフィールドにインデックスを作成するには?