首页 > 解决方案 > 如何从 node.js 网络服务中获取数据并在 iOS 应用程序中显示?

问题描述

我有一个内置在 node.js 中的 REST API 网络服务,我可以通过 url 访问它。我需要将该数据导入 iOS 应用程序。目前,我在 HomeModel.swift 中使用的代码以字节的形式为我提供数据,而不是预期的:

{
  "recordsets":[[{"USERCODE":"A", "PASSWORD":"SaYX^]Kc", "FIRSTNAME":"ADMINISTRATOR", "LASTNAME":" ", "USERCLASS":"1", "CONTACTNO":"2213"}]],
  "recordset":[{"USERCODE":"A", "PASSWORD":"SaYX^]Kc", "FIRSTNAME":"ADMINISTRATOR", "LASTNAME":" ", "USERCLASS":"1", "CONTACTNO":"2213"}],
  "output":{},
  "rowsAffected":[1]
}

此外,当我尝试解析数据时,出现错误:

无法将“__NSSingleObjectArrayI”(0x7fff87a90f58)类型的值转换为“NSString”(0x7fff87b502e8)。2020-08-18 20:15:02.512836+0300 [11444:1203386] 无法将“__NSSingleObjectArrayI”(0x7fff87a90f58)类型的值转换为“NSString”(0x7fff87b502e8)。

我的 HomeModel.swift 文件中的代码如下:

import UIKit

protocol HomeModelDelegate {
    
    func usersDownloaded(users:[User])
    
}

class HomeModel: NSObject {
    
    var delegate: HomeModelDelegate?
    let serviceUrl = "http://url.com/api/users"
    
    func getUsers() {
        
        //Download JSON data
        let url = URL(string: serviceUrl)
        
        if let url = url {
            //Create a url session
            let session = URLSession(configuration: .default)
            
            let task = session.dataTask(with: url) { (data, response, error) in
                
                if error == nil {
                    //Data successfully loaded and call the parseJson function on the data
                    self.parseJson(data: data!)
                    print(data!)
                }
                else {
                    //Some error occurred
                    print("Some error occurred")
                }
            }
            task.resume()
        }
        //Notify the viewcontroller and pass the data back
    }
    
    func parseJson(data: Data) {
        var usrArray = [User]()
        
        //Parse the data into User struct
        do {
            //Parse the data into JSON Object
            let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as! [String:String]
            
            //Loop through each result in the JSON array
            for jsonResult in jsonArray {
                
                //Caste JSON result as a dictionary
                let jsonDict = jsonResult as! [String : String]
                
                //Create new User and set its properties
                let usr = User(usrID: jsonDict["USERNAME"]!, firstName: jsonDict["FIRSTNAME"]!, lastName: jsonDict["LASTNAME"]!, password: jsonDict["PASSWORD"]!, usrClass: jsonDict["USERCLASS"]!)
                 
                //Add the user to the array
                usrArray.append(usr)
            }
            
            //Pass the data array back to the delegate
            delegate?.usersDownloaded(users: usrArray)
            
        }
        catch{
            //Some error occurred
            print("Some error occurred")
        }
    }
}

如何解决此问题并将“记录集”数据显示到我的控制台上?

标签: iosswift

解决方案


你得到的错误是因为方法中的这一行parseJson(data:)

let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as! [String:String]

这是为什么?

您正在尝试将您的 JSON 对象强制转换为键和值类型字符串的字典。键可能是字符串类型,但值肯定不是,这正是错误试图告诉你的。您正在尝试将 Array 转换为 String 永远不会发生的东西。

如果您查看上面粘贴的 JSON 中的第一个键值对,您可以看到键recordsets是 String 类型,但对于值,这不是真的。值是 Array 类型,将其转换为键和值类型 String 的字典将失败。

您如何解决此问题并在控制台中显示记录集值?

我会在可解码协议的帮助下解决这个问题。

首先,我将声明我的DataTransferObject并使其符合可解码协议。在那里,您可以看到属性与 JSON 中的键具有完全相同的名称。如果您拼错任何这些属性,解码 JSON 将失败,所以要小心。

struct DataTransferObject: Decodable {
    let recordsets: [[User]]
    let recordset: [User]
    let output: OutputObject
    let rowsAffected: [Int]
}

然后你必须声明你的DataTransferObjectall 中缺少的类型也符合 decodable 。因为User对象中的属性与 JSON 中的键命名不同,所以您必须使用CodingKeys枚举器将它们与 JSON 键相关联。

struct User: Decodable {
    let usrID: String
    let firstName: String
    let lastName: String
    let password: String
    let usrClass: String
    
    enum CodingKeys: String, CodingKey {
        case usrID = "USERCODE"
        case password = "PASSWORD"
        case firstName = "FIRSTNAME"
        case lastName = "LASTNAME"
        case usrClass = "USERCLASS"
    }
}

struct OutputObject: Decodable {
    
}

然后你可以修改你的parseJson(data:)方法来使用JSONDecoder对象来解码你的 JSON 和打印recordset属性到控制台。

func parseJson(data: Data) {
    do {
        // Instantiate JSONDecoder object
        let decoder = JSONDecoder()
        
        // Decode JSON to DataTransferObject
        let dto = try decoder.decode(DataTransferObject.self, from: data)
        
        // Print out recordset property
        print("*** Decoded recordset property: \(dto.recordset)")
        
    } catch {
        //Some error occurred
        print("Some error occurred: \(error.localizedDescription)")
    }
}

推荐阅读