如何解析本地 Json 并从 Json 对象 url 下载图像并将图像路径更新到 Model 类?

问题描述

我在本地关注 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 类。

问题说明:

解析后(在更新模型之前)我需要将“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,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,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
}