You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift解析根为二维数组的Sonos状态JSON报错,求解决方案

解决Swift解码Node-RED Sonos状态响应的问题

问题根源分析

你遇到的解码失败主要有两个核心原因:

  1. 结构体层级与JSON结构不匹配:你的Sonos响应是双层数组结构([[设备对象, 设备对象,...]]),但你定义的结构体多套了sonos、status这些不存在的嵌套层级,导致JSONDecoder找不到对应的键值对。
  2. 字段类型不一致:JSON里的urlPort字段既有数字类型(比如第一个设备的1400),又有字符串类型(其他设备的"1400"),而你直接定义成Int类型,遇到字符串时会直接解码失败。

修正后的解决方案

第一步:定义匹配JSON结构的设备结构体

我们需要定义一个直接对应设备对象的结构体,并通过自定义解码逻辑处理urlPort的类型不一致问题:

struct SonosDevice: Codable {
    let urlHostname: String
    let urlPort: Int
    let baseUrl: String
    let sonosName: String
    let uuid: String
    let invisible: Bool
    
    // 自定义解码逻辑,兼容Int和String类型的urlPort
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        urlHostname = try container.decode(String.self, forKey: .urlHostname)
        
        // 先尝试解码为Int,失败则解码为String再转Int
        do {
            urlPort = try container.decode(Int.self, forKey: .urlPort)
        } catch DecodingError.typeMismatch {
            let portString = try container.decode(String.self, forKey: .urlPort)
            guard let portInt = Int(portString) else {
                throw DecodingError.dataCorruptedError(
                    forKey: .urlPort,
                    in: container,
                    debugDescription: "urlPort必须是数字或可转换为数字的字符串"
                )
            }
            urlPort = portInt
        }
        
        baseUrl = try container.decode(String.self, forKey: .baseUrl)
        sonosName = try container.decode(String.self, forKey: .sonosName)
        uuid = try container.decode(String.self, forKey: .uuid)
        invisible = try container.decode(Bool.self, forKey: .invisible)
    }
    
    enum CodingKeys: String, CodingKey {
        case urlHostname, urlPort, baseUrl, sonosName, uuid, invisible
    }
}

第二步:使用正确的类型进行解码

因为JSON是双层数组,所以解码时要指定[[SonosDevice]].self,然后取出内层的设备数组即可:

do {
    let decodedResponse = try JSONDecoder().decode([[SonosDevice]].self, from: data)
    // 取出内层的设备列表(最外层数组通常只有一个元素)
    let sonosDevices = decodedResponse.first ?? []
    
    // 测试输出设备信息
    for device in sonosDevices {
        print("设备名称:\(device.sonosName) | IP:\(device.urlHostname) | 端口:\(device.urlPort)")
    }
} catch {
    print("解码失败:\(error.localizedDescription)")
}

额外说明

  • Swift的Codable对类型匹配要求严格,所以一定要确保结构体的字段名、层级和JSON完全对应,多余的嵌套层级会直接导致解码失败。
  • 如果后续urlPort的类型统一了(比如全是Int或全是String),可以去掉自定义解码逻辑,直接用默认的Codable实现即可。

内容的提问来源于stack exchange,提问作者Luca De Blasio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 13:27:57