json कुंजी के साथ कोडेबल का उपयोग करना जो कभी-कभी एक इंट और अन्य बार स्ट्रिंग है




swift swift4 (2)

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)
}

संपादित करें / अपडेट करें :

जब आप अपने एपीआई 0 लौटाते हैं, तो आप बस अपनी id nil असाइन कर सकते हैं:

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

मेरे पास एक एपीआई है जो कभी-कभी JSON में एक विशिष्ट कुंजी (इस मामले में id ) को एक इंट के रूप में लौटाएगा और दूसरी बार यह उसी कुंजी को एक स्ट्रिंग के रूप में लौटाएगा। उस JSON को पार्स करने के लिए मैं कोडेबल का उपयोग कैसे करूं?

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 । कारण यह है कि यह एक नंबर लौटाता है क्योंकि आईडी फ़ील्ड खाली है और जब आईडी फ़ील्ड खाली होती है तो यह 0 को एक आईडी के रूप में वापस करने के लिए डिफॉल्ट करता है जो कोडेबल एक नंबर के रूप में पहचान करता है। मैं मूल रूप से आईडी कुंजी को अनदेखा कर सकता हूं लेकिन कोड करने योग्य मुझे अपने ज्ञान को अनदेखा करने का विकल्प नहीं देता है। इससे निपटने का सबसे अच्छा तरीका क्या होगा?

यहाँ JSON है यह सुपर सरल है

काम कर रहे

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

त्रुटि - क्योंकि सिस्टम में कोई आईडी नहीं है, यह 0 को एक डिफ़ॉल्ट के रूप में देता है जो कोडेबल स्पष्ट रूप से स्ट्रिंग के विपरीत संख्या के रूप में देखता है।

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

यह MetadataType GeneralProduct साथ एक संभावित समाधान है, अच्छी बात यह है कि केवल 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
}




codable