Swift解析根为二维数组的Sonos状态JSON报错,求解决方案
解决Swift解码Node-RED Sonos状态响应的问题
问题根源分析
你遇到的解码失败主要有两个核心原因:
- 结构体层级与JSON结构不匹配:你的Sonos响应是双层数组结构(
[[设备对象, 设备对象,...]]),但你定义的结构体多套了sonos、status这些不存在的嵌套层级,导致JSONDecoder找不到对应的键值对。 - 字段类型不一致: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
相关产品推荐
相关产品推荐

