首页 > 解决方案 > 如何在 Swift 中正确解析带有根元素的 JSON 作为数组?

问题描述

我有以下问题,我没有正确的结构来解码来自 node-red 的响应以了解我的 Sonos 播放器的状态:

[[{"urlHostname":"192.168.1.1","urlPort":1400,"baseUrl":"http://192.168.1.1:1400","sonosName":"Sonos1","uuid":"RINCON_SONOS14001400","invisible":false},{"urlHostname":"192.168.1.2","urlPort":"1400","baseUrl":"http://192.168.1.2:1400","sonosName":"Sonos2","uuid":"RINCON_SONOS21400","invisible":false},{"urlHostname":"192.168.1.3","urlPort":"1400","baseUrl":"http://192.168.1.3:1400","sonosName":"Sonos3","uuid":"RINCON_SONOS31400","invisible":false},{"urlHostname":"192.168.1.4","urlPort":"1400","baseUrl":"http://192.168.1.4:1400","sonosName":"Sonos4","uuid":"RINCON_SONOS41400","invisible":false},{"urlHostname":"192.168.1.5","urlPort":"1400","baseUrl":"http://192.168.1.5:1400","sonosName":"Sonos5","uuid":"RINCON_SONOS51400","invisible":false},{"urlHostname":"192.168.1.6","urlPort":"1400","baseUrl":"http://192.168.1.6:1400","sonosName":"Sonos6","uuid":"RINCON_SONOS61400","invisible":false}]]

我的结构和解码调用如下所示:

typealias Response = [sonosData]

struct sonosData: Codable {
    let  sonos: [sonosStatusEntry]?
}

struct sonosStatusEntry: Codable {
    let status: [sonosStatus]?
}

struct sonosStatus: Codable {
    let urlHostname: String
    let urlPort: Int
    let baseUrl: String
    let sonosName: String
    let uuid: String
    let invisible: Bool
}

let response = try JSONDecoder().decode(Response.self, from: data)

我在 Swift 中收到以下错误:

Failed to load: The data couldn’t be read because it isn’t in the correct format.

有什么建议么?

标签: arraysjsonswiftjsondecoder

解决方案


问题之一与urlPort. 您的 json 具有IntString的值urlPort。您可以定义自定义初始化来处理它。

struct sonosStatus: Codable {
    let urlHostname: String
    let urlPort: Int
    let baseUrl: String
    let sonosName: String
    let uuid: String
    let invisible: Bool    
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        urlHostname = try values.decode(String.self, forKey: .urlHostname)
        baseUrl = try values.decode(String.self, forKey: .baseUrl)
        sonosName = try values.decode(String.self, forKey: .sonosName)
        uuid = try values.decode(String.self, forKey: .uuid)
        invisible = try values.decode(Bool.self, forKey: .invisible)
        
        // you can define urlPort as optional or unwrap it.
        if let intVal = try? values.decode(Int.self, forKey: .urlPort) {
            urlPort = intVal
        } else if let stringVal = try? values.decode(String.self, forKey: .urlPort) {
            urlPort = Int(stringVal) ?? 0
        } else {
            urlPort = ""
        }
    }
}

而且你的 json 是一个数组sonosStatus数组。

所以你需要修改Responsetypealias,如下所示:

typealias Response = [[sonosStatus]]

推荐阅读