如何解析本地 Json 并从 Json 对象 url 下载图像并将图像路径更新为模型 class?

How to Parse local Json and download the images from Json object url and update the image path to Model class?

我在本地

有关注Json
{
"country":[
      {
         "alpha2Code":"AF",
         "alpha3Code":"AFG",
         "flag":"https://raw.githubusercontent.com/DevTides/countries/master/afg.png",
         "name":"Afghanistan",
         "code":"+93"
      },
      {
         "alpha2Code":"AX",
         "alpha3Code":"ALA",
         "flag":"https://raw.githubusercontent.com/DevTides/countries/master/ala.png",
         "name":"Aland Islands",
         "code":"+358"
      },
      {
         "alpha2Code":"AL",
         "alpha3Code":"ALB",
         "flag":"https://raw.githubusercontent.com/DevTides/countries/master/alb.png",
         "name":"Albania",
         "code":"+355"
      }
]
}

我正在尝试加载此文件名并使用以下代码解析 Json

func readLocalJSONFile(forName name: String) -> Data? {
        do {
            if let filePath = Bundle.main.path(forResource: name, ofType: "json") {
                let fileUrl = URL(fileURLWithPath: filePath)
                let data = try Data(contentsOf: fileUrl)
                return data
            }
        } catch {
            print("error: \(error)")
        }
        return nil
    }
    
    func parseJson(jsonData: Data) -> countryCode? {
        do {
            let decodedData = try JSONDecoder().decode(sampleModel.self, from: jsonData)
            return decodedData
        } catch {
            print("error: \(error)")
        }
        return nil
    }

但这里它也更新了我的 sampleModel class。

问题陈述:

解析后(更新模型之前)我需要将“flag”键url图像下载到我的本地,然后在这个键“flag”中使用本地图像路径而不是url .

之后我想将这些数据添加到我的模型中。

知道我需要做哪些改变吗?

我刚刚对您的 parseJson 函数做了一些更改,请检查并告诉我。

class SOViewController: UIViewController {

//MARK:- Outlets

//MARK:- Variables
var arrImagesUrls = [URL]()
var arrCountries = [Country]()
var dictMainJson = [String:Any]()

//MARK:- UIViewController Methods
override func viewDidLoad() {
    super.viewDidLoad()
    let data = readLocalJSONFile(forName: "Country")
    let response = parseCodableJson(jsonData: data!)
    arrCountries = (response?.country)!
    print(arrCountries)
    arrImagesUrls.removeAll()
    for i in 0...arrCountries.count - 1{
        downloadFromServer(url: URL(string: arrCountries[i].flag!)!)
    }
    print(arrImagesUrls)
    changeFlagProperty(arrLocalUrls: arrImagesUrls)
}

//MARK:- Helpers
func readLocalJSONFile(forName name: String) -> Data? {
    do {
        if let filePath = Bundle.main.path(forResource: name, ofType: "json") {
            let fileUrl = URL(fileURLWithPath: filePath)
            let data = try Data(contentsOf: fileUrl)
            return data
        }
    } catch {
        print("error: \(error)")
    }
    return nil
}

func parseCodableJson(jsonData: Data) -> Response? {
    do {
        let decodedData = try JSONDecoder().decode(Response.self, from: jsonData)
        let jsonResult = try JSONSerialization.jsonObject(with: jsonData, options: .mutableLeaves)
        let result = jsonResult as! [String : Any]
        dictMainJson = result
        return decodedData
    } catch {
        print("error: \(error)")
    }
    return nil
}

//MARK:- Get Directory Path
func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let documentsDirectory = paths[0]
    let appURL = documentsDirectory.appendingPathComponent("APP_NAME")
    if !FileManager.default.fileExists(atPath: appURL.path) {
        try! FileManager.default.createDirectory(at: appURL, withIntermediateDirectories: true, attributes: nil)
    }
    return appURL
}

//MARK:- Download Zip From Server
func downloadFromServer(url:URL) {
    let zipFileName = url.lastPathComponent
    let downloadPath = self.getDocumentsDirectory()
    
    let newFolder = downloadPath.appendingPathComponent("Flag")
    if !FileManager.default.fileExists(atPath: newFolder.path) {
        do {
            try FileManager.default.createDirectory(atPath: newFolder.path, withIntermediateDirectories: true, attributes: nil)
        } catch let error {
            print(error.localizedDescription)
        }
    }
    let fileUrl = newFolder.appendingPathComponent(zipFileName)

    if FileManager.default.fileExists(atPath: fileUrl.path) {
        print("FILE AVAILABLE")
        //get images from local folder
        arrImagesUrls.append(fileUrl)
    } else {
        print("FILE NOT AVAILABLE")
        let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
        let downloadTask = urlSession.downloadTask(with: url)
        downloadTask.resume()
    }
}

func changeFlagProperty(arrLocalUrls:[URL]) {
    let arrDict : [[String:Any]] = (dictMainJson["country"] as? [[String:Any]])!
    var arrDicts = [[String:Any]]()
    for (i,dict) in arrDict.enumerated() {
        var dictData = dict
        dictData.updateValue("\(arrLocalUrls[i])", forKey: "flag")
        arrDicts.append(dictData)
    }
    dictMainJson["country"] = arrDicts
    if let jsonData = try? JSONSerialization.data(withJSONObject: dictMainJson,options: []) {
        print(jsonData)
        let response = parseCodableJson(jsonData: jsonData)
        arrCountries = (response?.country)!
        print(arrCountries)
    }
}
}

extension SOViewController : URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
    print("File Downloaded Location- ",  location)
    
    guard let url = downloadTask.originalRequest?.url else {
        return
    }
    
    let downloadPath = self.getDocumentsDirectory()
    let newFolder = downloadPath.appendingPathComponent("Flag")
    if !FileManager.default.fileExists(atPath: newFolder.path) {
        do {
            try FileManager.default.createDirectory(atPath: newFolder.path, withIntermediateDirectories: true, attributes: nil)
        } catch let error {
            print(error.localizedDescription)
            return
        }
    }
    
    let fileUrl = newFolder.appendingPathComponent(url.lastPathComponent)
    
    if !FileManager.default.fileExists(atPath: fileUrl.path) {
        do{
            try FileManager.default.copyItem(at: location, to: fileUrl)
            print("File Downloaded Location- \(fileUrl)" )
            arrImagesUrls.append(fileUrl)
        }catch let error {
            print("Copy Error: \(error.localizedDescription)")
        }
    }else {
        print("File Downloaded Location- \(fileUrl)" )
        arrImagesUrls.append(fileUrl)
    }
}
}

Response.swift

import Foundation

struct Response : Codable {

        let country : [Country]?

        enum CodingKeys: String, CodingKey {
                case country = "country"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                country = try values.decodeIfPresent([Country].self, forKey: .country)
        }

}

Country.swift

import Foundation

struct Country : Codable {

        let alpha2Code : String?
        let alpha3Code : String?
        let code : String?
        let flag : String?
        let name : String?

        enum CodingKeys: String, CodingKey {
                case alpha2Code = "alpha2Code"
                case alpha3Code = "alpha3Code"
                case code = "code"
                case flag = "flag"
                case name = "name"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                alpha2Code = try values.decodeIfPresent(String.self, forKey: .alpha2Code)
                alpha3Code = try values.decodeIfPresent(String.self, forKey: .alpha3Code)
                code = try values.decodeIfPresent(String.self, forKey: .code)
                flag = try values.decodeIfPresent(String.self, forKey: .flag)
                name = try values.decodeIfPresent(String.self, forKey: .name)
        }

}

我尝试了以下方法,现在效果很好

struct CountryCodeList : Decodable {
    var alpha2Code: String?
    var alpha3Code: String?
    var flag      : String?
    var name      : String?
    var code      : String?
}

public struct CountryCodeListModel : Decodable {
    var data      : [CountryCodeList]?
}

private var completionBlocks: ((String) -> Void)?
var cclm: CountryCodeListModel?


//Method to load json

func readLocalJSONFile(forName name: String) {
    do {
        if let filePath = Bundle.main.path(forResource: name, ofType: "json") {
            let fileUrl = URL(fileURLWithPath: filePath)
            let data = try Data(contentsOf: fileUrl)
            if let countryCodeObject = parse(jsonData: data) {
                cclm = countryCodeObject
                print(cclm?.data?[1].alpha2Code ?? "")  //Printing Correct Value
            }
        }
    } catch {
        print("error: \(error)")
    }
}



func parse(jsonData: Data) -> CountryCodeListModel?{
    var dataArray : [Dictionary<String,Any>] = [[:]]
    var country = Dictionary<String,Any>()
    var modelData = Dictionary<String,Any>()
    do {
        // make sure this JSON is in the format we expect
        if let json = try JSONSerialization.jsonObject(with: jsonData, options: []) as? Dictionary<String,Any> {
            dataArray.removeAll()
            for item  in json["data"] as! [Dictionary<String, Any>] {
                country = item
                
                let url = URL(string: country["flag"] as? String ?? "")
                let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
                let image = UIImage(data: data!)
                let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
                let fileName = url?.lastPathComponent // name of the image to be saved
                let fileURL = documentsDirectory.appendingPathComponent(fileName ?? "")
                if let data = image?.jpegData(compressionQuality: 1.0){
                    do {
                        try data.write(to: fileURL)
                        country["flag"] = fileURL.absoluteString
                        //print("file saved")
                        //urlAsString = fileURL.absoluteString
                    } catch {
                        print("error saving file:", error)
                    }
                }
                
                dataArray.append(country)
                country.removeAll()
                
                    
                
            }
            modelData["data"] = dataArray
            //print(modelData)
            let jsonData1 = try JSONSerialization.data(withJSONObject: modelData, options: [])
            
            do {
                    let decodedData = try JSONDecoder().decode(CountryCodeListModel.self, from: jsonData1)
                
                    return decodedData
                } catch {
                    print("error: \(error)")
                }
            
        }
    } catch let error as NSError {
        print("Failed to load: \(error.localizedDescription)")
    }
    return nil
}