2017-12-22 9 views
2

JSONで特定のキー(この場合はid)をIntとして返すAPIがあります。文字列。 JSONを解析するためにcodableを使用するにはどうすればよいですか?コードでコードを使用することがありますが、時にはIntやその他の文字列を返します。

struct GeneralProduct: Codable { 
    var price:Double! 
    var id:String? 
    var name:String! 

    private enum CodingKeys: String, CodingKey { 
     case price = "p" 
     case id = "i" 
     case name = "n" 
    } 

    init(price:Double? = nil, id: String? = nil, name: String? = nil) { 
     self.price = price 
     self.id = id 
     self.name = name 
    } 
} 

このエラーメッセージが表示され続ける:Expected to decode String but found a number instead idフィールドが空であるためidフィールドが空の場合、デフォルトでは、codableが数字として識別するIDとして0が返されます。私は基本的にIDキーを無視することができますが、コード可能では私の知識にそれを無視するオプションを与えていません。これを処理する最良の方法は何でしょうか?

ここにJSONがあります。それは

{ 
    "p":2.12, 
    "i":"3k3mkfnk3", 
    "n":"Blue Shirt" 
} 

エラー作業

超簡単です - 何のIDがシステムに存在しないので、それは明らかにコード化可能な文字列ではなく数値として見ているデフォルトとして0を返します。

{ 
    "p":2.19, 
    "i":0, 
    "n":"Black Shirt" 
} 
+0

私はあなたに私たちをお勧めしますe SwiftyJSONライブラリ。 – t4nhpt

+1

なぜそれを言うのですか?私は本当にコード練習がはるかに優れているので、SwiftyJSONを使うのが良い習慣だとは思わない。解決策がない場合は、明らかにSwiftyJSONを使用しますが、これが実行される方法がなければ私は驚くでしょう。 –

+0

どのJSONが動作し、どのJSONが動作しないかの例を挙げることができますか? –

答えて

1
struct GeneralProduct: Codable { 
    var price: Double? 
    var id: String? 
    var name: String? 
    private enum CodingKeys: String, CodingKey { 
     case price = "p", id = "i", name = "n" 
    } 
    init(price: Double? = nil, id: String? = nil, name: String? = nil) { 
     self.price = price 
     self.id = id 
     self.name = name 
    } 
    init(from decoder: Decoder) throws { 
     let container = try decoder.container(keyedBy: CodingKeys.self) 
     price = try container.decode(Double.self, forKey: .price) 
     name = try container.decode(String.self, forKey: .name) 
     if let value = try? container.decode(Int.self, forKey: .id) { 
      id = String(value) 
     } else { 
      id = try container.decode(String.self, forKey: .id) 
     } 
    } 
} 

let json1 = """ 
{ 
"p":2.12, 
"i":"3k3mkfnk3", 
"n":"Blue Shirt" 
} 
""" 

let json2 = """ 
{ 
"p":2.12, 
"i":0, 
"n":"Blue Shirt" 
} 
""" 

do { 
    let product = try JSONDecoder().decode(GeneralProduct.self, from: Data(json2.utf8)) 
    print(product.price ?? "") 
    print(product.id ?? "") 
    print(product.name ?? "") 
} catch { 
    print(error) 
} 

編集/更新

また、単純にを割り当てることができますあなたのidからはあなたのAPIが0返すとき:

if let _ = try? container.decode(Int.self, forKey: .id) { 
    id = nil 
} else { 
    id = try container.decode(String.self, forKey: .id) 
} 
+0

私はこれが私が探していると信じていますが、実際のシナリオははるかに複雑で、はるかに多くの変数があります。各変数のカスタムイニシャライザを記述することなくこれを行う方法はありませんか?それは非常に小さなもののための余分なコードの多くです。 –

+0

私はあなたがカスタムデコーダなしでそれを解決できるとは思わない –

+0

良い音良いです。完全に理解する。あなたが投稿された他の答えのあなたの考えは何ですか? –

1

これはMetadataTypeと可能な解決策である、素敵な事はそれがないGeneralProductのためだけではなく、すべてのstruct同じ曖昧さを持つための一般的な解決策になることができます:

struct GeneralProduct: Codable { 
    var price:Double? 
    var id:MetadataType? 
    var name:String? 

    private enum CodingKeys: String, CodingKey { 
    case price = "p" 
    case id = "i" 
    case name = "n" 
    } 

    init(price:Double? = nil, id: MetadataType? = nil, name: String? = nil) { 
    self.price = price 
    self.id = id 
    self.name = name 
    } 
} 

enum MetadataType: Codable { 
    case int(Int) 
    case string(String) 

    init(from decoder: Decoder) throws { 
    let container = try decoder.singleValueContainer() 
    do { 
     self = try .int(container.decode(Int.self)) 
    } catch DecodingError.typeMismatch { 
     do { 
     self = try .string(container.decode(String.self)) 
     } catch DecodingError.typeMismatch { 
     throw DecodingError.typeMismatch(MetadataType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload not of an expected type")) 
     } 
    } 
    } 

    func encode(to encoder: Encoder) throws { 
    var container = encoder.singleValueContainer() 
    switch self { 
    case .int(let int): 
     try container.encode(int) 
    case .string(let string): 
     try container.encode(string) 
    } 
    } 
} 

これはテストです:

let decoder = JSONDecoder() 
var json = "{\"p\":2.19,\"i\":0,\"n\":\"Black Shirt\"}" 
var product = try! decoder.decode(GeneralProduct.self, from: json.data(using: .utf8)!) 
if let id = product.id { 
    print(id) // 0 
} 

json = "{\"p\":2.19,\"i\":\"hello world\",\"n\":\"Black Shirt\"}" 
product = try! decoder.decode(GeneralProduct.self, from: json.data(using: .utf8)!) 
if let id = product.id { 
    print(id) // hello world 
} 
関連する問題