Handling incrementing JSON name using Swift

那年仲夏 提交于 2020-05-08 19:57:09

问题


I have a JSON object with incrementing names to parse and I want to store the output into an object with a name field and a list of pet field. I normally use JSONDecoder as its pretty handy and easy to use, but I don't want to hard-code the CodingKey as I think it is very bad practice.

Input:

{"shopName":"KindHeartVet", "pet1":"dog","pet2":"hamster","pet3":"cat",  ...... "pet20":"dragon"}

The object that I want to store the result in is something like the following.

class VetShop: NSObject, Decodable {
var shopName: String?
var petList: [String]?

private enum VetKey: String, CodingKey {
    case shopName
    case petList
}

required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: VetKey.self)
    shopName = try? container.decode(String.self, forKey: .shopName)

    // implement storing of petList here.
}

}

What I'm struggling a lot on is, as CodingKey is enum, its a let constants, so I can't modify (and shouldn't modify) a constant, but I need to map the petList to the "petN" field, where N is the incrementing number.

EDIT :

I definitely cannot change the API response structure because it is a public API, not something I developed, I'm just trying to parse and get the value from this API, hope this clear the confusion!


回答1:


Codable has provisions for dynamic keys. If you absolutely can't change the structure of the JSON you're getting, you could implement a decoder for it like this:

struct VetShop: Decodable {
    let shopName: String
    let pets: [String]

    struct VetKeys: CodingKey {
        var stringValue: String
        var intValue: Int?
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        init?(intValue: Int) {
            self.stringValue = "\(intValue)";
            self.intValue = intValue
        }
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: VetKeys.self)
        var pets = [String]()
        var shopName = ""
        for key in container.allKeys {
            let str = try container.decode(String.self, forKey: key)
            if key.stringValue.hasPrefix("pet") {
                pets.append(str)
            } else {
                shopName = str
            }
        }
        self.shopName = shopName
        self.pets = pets
    }
}



回答2:


You can try this to parse your data as Dictionary. In this way, you can get all keys of the dictionary.

    let url = URL(string: "YOUR_URL_HERE")
    URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
        guard let data = data, error == nil else { return }
        do {
            let dics = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! Dictionary<String, Any>
            let keys = [String](dics.keys)
            print(keys) // You have the key list
            print(dics[keys[0]]) // this will print the first value
         } catch let error as NSError {
            print(error)
        }
    }).resume() 

I hope you can figure out what you need to do.



来源:https://stackoverflow.com/questions/61400748/handling-incrementing-json-name-using-swift

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!