2017-12-07 21 views
1

外部ソースから受信したJSONを解析するためにDecodableプロトコルを使用しています。私が知っている属性をデコードした後も、まだ知られておらず、まだデコードされていないいくつかの属性がJSONに存在する可能性があります。たとえば、外部ソースが将来の時点でJSONに新しい属性を追加した場合、これらの未知の属性を[String: Any]ディクショナリ(または代替)に保存して値を無視しないようにしたいと考えています。Swift 4のDecoderコンテナから非コード化属性を取得する方法は?

問題は、私が知っている属性をデコードした後に、デコードされていない属性を取得するためのコンテナにアクセサがないことです。私はdecoder.unkeyedContainer()を知っていますが、各値を繰り返し処理することはできますが、これは私の場合は機能しません。なぜなら、どの値型を繰り返し処理しているのですか?JSON必ずしも同一ではない。ここで

は私が達成しようとしている何のための遊び場での例です:

// Playground 
import Foundation 

let jsonData = """ 
{ 
    "name": "Foo", 
    "age": 21 
} 
""".data(using: .utf8)! 

struct Person: Decodable { 
    enum CodingKeys: CodingKey { 
     case name 
    } 

    let name: String 
    let unknownAttributes: [String: Any] 

    init(from decoder: Decoder) throws { 
     let container = try decoder.container(keyedBy: CodingKeys.self) 
     self.name = try container.decode(String.self, forKey: .name) 

     // I would like to store the `age` attribute in this dictionary 
     // but it would not be known at the time this code was written. 
     self.unknownAttributes = [:] 
    } 
} 

let decoder = JSONDecoder() 
let person = try! decoder.decode(Person.self, from: jsonData) 

// The `person.unknownAttributes` dictionary should 
// contain the "age" attribute with a value of 21. 

unknownAttributes辞書はage属性と値この場合は、他の可能な値を格納するために、私は希望 将来外部ソースからJSONに追加される場合は、型を指定します。

このようなことをしたい理由は、JSONに存在する未知の属性を保持できるため、属性キーがわかってから適切にコードを更新できるようにすることです。

私はStackOverflowとGoogleでたくさんの検索を行ってきましたが、まだこのユニークなケースに遭遇していません。前もって感謝します!

答えて

1

君たちは...スウィフト4コーディングAPIを強調するための新規な方法を考え出す続ける;)

一般解、すべて値の種類をサポートし、できない場合があります。しかし、プリミティブ型のために、あなたはこれを試すことができます。

を文字列ベースのキーを使用して簡単なCodingKeyタイプを作成します。

struct UnknownCodingKey: CodingKey { 
    init?(stringValue: String) { self.stringValue = stringValue } 
    let stringValue: String 

    init?(intValue: Int) { return nil } 
    var intValue: Int? { return nil } 
} 

を次に、上記UnknownCodingKeyをキーと標準KeyedDecodingContainerを用いた一般的なデコード機能を書きます

:最後に
func decodeUnknownKeys(from decoder: Decoder, with knownKeys: Set<String>) throws -> [String: Any] { 
    let container = try decoder.container(keyedBy: UnknownCodingKey.self) 
    var unknownKeyValues = [String: Any]() 

    for key in container.allKeys { 
     guard !knownKeys.contains(key.stringValue) else { continue } 

     func decodeUnknownValue<T: Decodable>(_ type: T.Type) -> Bool { 
      guard let value = try? container.decode(type, forKey: key) else { 
       return false 
      } 
      unknownKeyValues[key.stringValue] = value 
      return true 
     } 
     if decodeUnknownValue(String.self) { continue } 
     if decodeUnknownValue(Int.self) { continue } 
     if decodeUnknownValue(Double.self) { continue } 
     // ... 
    } 
    return unknownKeyValues 
} 

は、あなたのunknownAttributes辞書を埋めるためにdecodeUnknownKeys機能を使用します

struct Person: Decodable { 
    enum CodingKeys: CodingKey { 
     case name 
    } 

    let name: String 
    let unknownAttributes: [String: Any] 

    init(from decoder: Decoder) throws { 
     let container = try decoder.container(keyedBy: CodingKeys.self) 
     self.name = try container.decode(String.self, forKey: .name) 

     let knownKeys = Set(container.allKeys.map { $0.stringValue }) 
     self.unknownAttributes = try decodeUnknownKeys(from: decoder, with: knownKeys) 
    } 
} 

簡単なテスト:

let jsonData = """ 
{ 
    "name": "Foo", 
    "age": 21, 
    "token": "ABC", 
    "rate": 1.234 
} 
""".data(using: .utf8)! 

let decoder = JSONDecoder() 
let person = try! decoder.decode(Person.self, from: jsonData) 
print(person.name) 
print(person.unknownAttributes) 

プリント:

フー
[ "年齢":21、 "トークン": "ABC"、 "レート":1.234]

関連する問題