简体   繁体   中英

check the json response is array or int or string for a key?

I am having the json response in which "products" key sometime having the int value and some cases it had an array? How to check whether it is having array or Int?

"products": 25

or

"products": [77,80,81,86]

I am using this

self.productsCount = mResp["products"] as! [Int]

but it is crashed every time when it is not having array.

Now i am not getting how to check this because i have the different option for Int and Array?

Please help me.Thanks

It crashes because you force unwrap as an Integer Array, even though you just have an integer. The solution is to check for both:

self.productsCount = mResp["products"] as? [Int] ?? mResp["products"] as? Int

Other Solution

if let proCount = mResp["products"] as? [Int] { 
  self.productsCount = proCount
} else {
  self.productsCount = mResp["products"] as? Int
}

There is no need to fall back to Any here. Even problematic JSON like this can be handled with Codable . You just need to keep trying the different types until one works.

struct Thing: Decodable {
    let products: [Int]

    enum CodingKeys: String, CodingKey {
        case products
    }

    init(from decoder: Decoder) throws {
        // First pull out the "products" key
        let container = try decoder.container(keyedBy: CodingKeys.self)

        do {
            // Then try to decode the value as an array
            products = try container.decode([Int].self, forKey: .products)
        } catch {
            // If that didn't work, try to decode it as a single value
            products = [try container.decode(Int.self, forKey: .products)]
        }
    }
}


let singleJSON = Data("""
{ "products": 25 }
""".utf8)

let listJSON = Data("""
{ "products": [77,80,81,86] }
""".utf8)

let decoder = JSONDecoder()

try! decoder.decode(Thing.self, from: singleJSON).products   // [25]
try! decoder.decode(Thing.self, from: listJSON).products     // [77, 80, 81, 86]

This is temporary solution as you want. Check for possible type with "Any" type.

    var anyType : Any!
    anyType = "123"
    anyType = ["Test","Test1"]
    anyType = 1
    if anyType is Array {
        print("is Array")
    }else if anyType is String {
        print("is String")
    }else if anyType is Int {
        print("is Int")
    }

let's assume your json name is jsonData

Check for Int and Array Int :

if let intVal = jsonData["products"] as? Int {
    print("Products is a Integer: ", intVal)

} else if let jsonArr = jsonData["products"] as? [Int] {

    var intVals = [Int]()
    for json in jsonArr {
        intVals.append(json)
    }
    print("Json is array of Int: ", intVals)
}
let dict = [77,80,81,86]//Pass your parameter or parsed json value
 if dict is Array<Any> {
    print("Yes, it's an Array")
}
else{
      print("NO, it's not an Array")

}

Generic solution would be like this,

let products = mResp["products"] as? Any
if let item = products as? [Int] {
    print("array", item)
} else if let item = products as? Int {
    print("Integer", item)
}

Use Generics for obtaining a better solution and provide the type at the time of decoding this model.

struct Product<T: Codable>: Codable {
    let products: T?
}

And you can use it with nested try catch :

do {
    let product = try JSONDecoder().decode(Product<Int>.self, from: data)
    print(product)
} catch {
    do {
        let product = try JSONDecoder().decode(Product<[Int]>.self, from: data)
        print(product)
    } catch {
        print(error)
    }
}

Note: This solution assumes there is not more than a few different type-varying properties in the codable struct. If there are multiple type-varying properties I'd recommend use a custom init(decoder:) as provided in the accepted answer which would be much better design instead of having a try-catch tree.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM