Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
228 views
in Technique[技术] by (71.8m points)

ios - valueNotFound Error when getting back an API call into JSON

When I do an API call and get data back it cannot be decoded into my struct properly.

The error I get is:

failed to convert valueNotFound(Swift.Int, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "newDeathsByDeathDate", intValue: nil)], debugDescription: "Expected Int value but found null instead.", underlyingError: nil))

My Code is:

override func viewDidLoad() {
        super.viewDidLoad()
        
        let url = """
https://api.coronavirus.data.gov.uk/v1/data?filters=areaType=nation;areaName=england&structure=%7b%22date%22:%22date%22,%22areaName%22:%22areaName%22,%22areaCode%22:%22areaCode%22,%22newCasesByPublishDate%22:%22newCasesByPublishDate%22,%22cumCasesByPublishDate%22:%22cumCasesByPublishDate%22,%22newDeathsByDeathDate%22:%22newDeathsByDeathDate%22,%22cumDeathsByDeathDate%22:%22cumDeathsByDeathDate%22%7d
"""
        print(url)
        print(NSURL(string: url))
        getData(from: url)
        
        // Do any additional setup after loading the view.
    }
    
    private func getData(from url: String) {
        guard let theURL = URL(string: url) else { print ("oops"); return }
        let getfromurl = URLSession.shared.dataTask(with: theURL, completionHandler: {data, response, error in
            guard let data = data, error == nil else{
                print("Something Went Wrong")
                return
            }
            
            //Have data
            var result: Response?
            do {
                result = try JSONDecoder().decode(Response.self, from: data)
            }
            catch{
                print("failed to convert (error)")
            }
            
            guard let json = result else {
                return
            }
            
            print(json.data)
        })
        getfromurl.resume()
    
    }
    
    
}

My Structs are:

struct Response: Codable {
    let data: [MyData]
}

struct MyData: Codable{
    let date: String
    let areaName: String
    let areaCode: String
    let newCasesByPublishDate: Int
    let cumCasesByPublishDate: Int
    let newDeathsByDeathDate: Int
    let cumDeathsByDeathDate: Int
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Please look closely at the JSON, the values for keys newDeathsByDeathDate and cumDeathsByDeathDate can be null.

Make the type of the corresponding struct members optional

struct MyData: Codable{
    let date: String
    let areaName: String
    let areaCode: String
    let newCasesByPublishDate: Int
    let cumCasesByPublishDate: Int?
    let newDeathsByDeathDate: Int?
    let cumDeathsByDeathDate: Int?
}

Alternatively – to keep non-optional values – decode the JSON manually and set the values to 0 if they are null


struct MyData : Decodable {
    let date, areaName, areaCode: String
    let newCasesByPublishDate, cumCasesByPublishDate : Int
    let newDeathsByDeathDate, cumDeathsByDeathDate: Int
    
    private enum CodingKeys : String, CodingKey { case date, areaName, areaCode, newCasesByPublishDate, cumCasesByPublishDate, newDeathsByDeathDate, cumDeathsByDeathDate }
    
    init(from decoder : Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        date = try container.decode(String.self, forKey: .date)
        areaName = try container.decode(String.self, forKey: .areaName)
        areaCode = try container.decode(String.self, forKey: .areaCode)
        newCasesByPublishDate = (try? container.decode(Int.self, forKey: .newCasesByPublishDate)) ?? 0
        cumCasesByPublishDate = (try? container.decode(Int.self, forKey: .cumCasesByPublishDate)) ?? 0
        newDeathsByDeathDate = (try? container.decode(Int.self, forKey: .newDeathsByDeathDate)) ?? 0
        cumDeathsByDeathDate = (try? container.decode(Int.self, forKey: .cumDeathsByDeathDate)) ?? 0
    }
}

And again, don't ignore useful error messages by printing something meaningless

Replace

print("Something Went Wrong")

with

print("An error occured:", error!)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...