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

Ios 如何解析本地Json,从Json对象url下载图像,并将图像路径更新到模型类?,ios,json,swift,url,uiimage,Ios,Json,Swift,Url,Uiimage,我的本地版本中有以下Json { "country":[ { "alpha2Code":"AF", "alpha3Code":"AFG", "flag":"https://raw.githubusercontent.com/DevTides/countries/master/afg.png",

我的本地版本中有以下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, 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)
    }
}
}
响应。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)
        }

}
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)
        }

}
国家。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)
        }

}
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
}
struct countrycode列表:可解码{
var alpha2Code:字符串?
var alpha3Code:字符串?
var标志:字符串?
变量名称:字符串?
变量代码:字符串?
}
公共结构CountryCodeListModel:可解码{
变量数据:[CountryCodeList]?
}
私有变量completionBlocks:((字符串)->Void)?
var cclm:CountryCodeListModel?
//方法来加载json
func readLocalJSONFile(forName:String){
做{
如果让filePath=Bundle.main.path(forResource:name,of type:“json”){
让fileUrl=URL(fileURLWithPath:filePath)
let data=try data(contentsOf:fileUrl)
如果让countryCodeObject=parse(jsonData:data){
cclm=countryCodeObject
打印(cclm?.data?[1].alpha2Code???“”//打印正确的值
}
}
}抓住{
打印(“错误:\(错误)”)
}
}
func parse(jsonData:Data)->CountryCodeListModel{
var dataArray:[字典]=[[:]]
var country=字典()
var modelData=字典()
做{
//确保此JSON采用我们期望的格式
如果让json=try JSONSerialization.jsonObject(使用:jsonData,选项:[])作为字典{
dataArray.removeAll()
对于json[“数据”]中的项作为![字典]{
国家=项目
设url=url(字符串:国家[“标志”]为?字符串??)
let data=try?data(contentsOf:url!)//确保此url中的图像确实存在,否则在if-let-check/try-catch中展开
让image=UIImage(数据:data!)
让documentsDirectory=FileManager.default.URL(对于:.documentDirectory,在:.userDomainMask中)。首先!
让fileName=url?.lastPathComponent//要保存的图像的名称
让fileURL=documentsDirectory.appendingPathComponent(文件名??“”)
如果let data=image?.jpeg数据(压缩质量:1.0){
做{
尝试数据写入(到:fileURL)
国家[“标志”]=fileURL.absoluteString
//打印(“文件已保存”)
//urlAsString=fileURL.absoluteString
}抓住{
打印(“保存文件时出错:”,错误)
}
}
dataArray.append(国家/地区)
country.removeAll()
}
modelData[“数据”]=数据数组
//打印(模型数据)
让jsonData1=尝试JSONSerialization.data(使用jsonObject:modelData,选项:[])
做{
让decodedData=try JSONDecoder().decode(CountryCodeListModel.self,from:jsonData1)
返回解码数据
}抓住{
打印(“错误:\(错误)”)
}
}
}将let错误捕获为NSError{
打印(“未能加载:\(错误。localizedDescription)”)
}
归零
}

将图像保留为url并使用第三方解决方案(如king fisher或nuke)允许将url传递到UIImageView并不复杂。此后,我们不需要任何互联网依赖性,这就是将其保留在localKingFisher、Alamofire+image和SDWebImage中的原因,SDWebImage可以使用缓存。如果需要,您应该能够在本地保存它们。请参阅等。管理您自己如何从“缓存”中检索(如果存在)。我正在尝试减少第三方的使用按如下方式使用https://raw.githubusercontent.com/DevTides/countries/master/afg.png)No,不要使用同步下载(数据(contentOf)),这将需要更长的时间。使用URLSession代替标题我怀疑这是否有效,因为在这里,我们是分开的图像下载部分,稍后我们需要更新模型,所以这将是一个艰难的方式,否则你可以做它以及在细胞等待让我更新这个与model@JoakimDanielson谢谢您的建议:)@JOhnMic我检查了您的新问题,但您将在我的解决方案中获得所有解决方案。让我来引导你。