arrays - Swift 4.1 可编码/可解码嵌套数组和字典
问题描述
需要更多关于 json 的帮助,关于如何布局的任何建议?
是否应该将players_loop 添加到我放置的位置,等等... 绕了一段时间。
结构:
struct LMSRequest: Decodable {
let id : Int
let method : String
let result : [String:Result]
let params : [Param]
}
struct players_loop: Decodable {
let uuid : String
let ip : String
let playerid : String
let connected : String
let firmware : String
let displaytype : String
let power : Int
let name : String
let playerindex : String
let canpoweroff : Int
let isplayer : Int
let seq_no : Int
let isplaying : Int
let modelname : String
let model : String
}
enum Result: CustomStringConvertible {
case string(String)
case int(Int)
case array([Result])
case players_loop([players_loop])
var description: String {
switch self {
case let .string(string): return string
case let .int(int): return "\(int)"
case let .players_loop(players_loop): return "\(players_loop)"
//case let .double(double): return "\(double)"
//case let .number(number): return "\(number)"
case let .array(array): return "\(array)"
}
}
}
extension Result: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self = .string(string)
} else if let int = try? container.decode(Int.self) {
self = .int(int)
//} else if let double = try? container.decode(Double.self) {
//self = .double(double)
} else {
self = .array(try container.decode([Result].self))
}
}
}
extension Result {
var stringValue: String? { if case let .string(value) = self { return value } else { return nil } }
var intValue: Int? { if case let .int(value) = self { return value } else { return nil } }
//var doubleValue: Double? { if case let .double(value) = self { return value } else { return nil } }
var arrayValue: [Result]? { if case let .array(value) = self { return value } else { return nil } }
subscript(_ index: Int) -> Result? {
return arrayValue?[index]
}
}
enum Param: CustomStringConvertible {
case string(String)
case int(Int)
case array([Param])
var description: String {
switch self {
case let .string(string): return string
case let .int(int): return "\(int)"
case let .array(array): return "\(array)"
}
}
}
extension Param: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self = .string(string)
} else if let int = try? container.decode(Int.self) {
self = .int(int)
} else {
self = .array(try container.decode([Param].self))
}
}
}
extension Param {
var stringValue: String? { if case let .string(value) = self { return value } else { return nil } }
var intValue: Int? { if case let .int(value) = self { return value } else { return nil } }
var arrayValue: [Param]? { if case let .array(value) = self { return value } else { return nil } }
subscript(_ index: Int) -> Param? {
return arrayValue?[index]
}
}
json:
let json = """
{
"id": 1,
"method": "slim.request",
"result": {
"mac": "B8:27:EB:7A:FA:4B",
"uuid": "e54389e6-7f63-4aac-aa83-a199226279f4",
"other player count": 0,
"info total artists": 6018,
"version": "7.9.1",
"info total albums": 6327,
"info total songs": 71808,
"lastscan": "1528917689",
"info total genres": 553,
"player count": 2,
"players_loop": [
{
"model": "squeezelite",
"modelname": "SqueezeLite",
"seq_no": 0,
"isplaying": 1,
"isplayer": 1,
"canpoweroff": 1,
"connected": 1,
"firmware": "v1.8.7-1052",
"playerindex": "-",
"displaytype": "none",
"power": 1,
"name": "piCorePlayer",
"playerid": "b8:27:eb:7d:09:80",
"uuid": null,
"ip": "10.2.2.21:47100"
}
]
},
"params": [
"b8:27:eb:db:6d:62",
[
"serverstatus",
"-",
1,
"tags:GPASIediqtymkovrfijnCYXRTIuwxNlasc"
]
]
}
""".data(using: .utf8)!
测试:
do {
let decoder = JSONDecoder()
//decoder.keyDecodingStrategy = .convertFromSnakeCase
let lms = try decoder.decode(LMSRequest.self, from: json)
print(lms)
let inner = lms.params[1]
print(type(of: lms.params[1][0]))
print(inner[3] as Any)
} catch let jsonErr {
print("Failed to decode:", jsonErr)
}
解码失败:typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), _DictionaryCodingKey(stringValue: "players_loop", intValue: nil), _JSONKey(stringValue: " Index 0", intValue: 0)], debugDescription: "期望解码 Array 但找到了一个字典。",底层错误: nil))
参数部分效果很好,很重要,在这里回答: Swift 4.1 Codable/Decodable Nested Array
所以我使用参数作为“结果”的起点——>但现在尝试添加 player_loop。
谢谢!!!
https://gist.github.com/sckramer/5b90d3b15eef2c19196abab2d7698e70
解决方案
你有几个问题。首先,您需要了解这些optional
字段。从您附加的 JSON 中,可以清楚地看到uuid
insideplayers_loop
可以为空。所以这个字段在你的模型中也应该是可选的。
我故意更改了结构的名称以反映 Swift 标识符约定(类型的名称应以UpperCase开头)。
struct PlayersLoop: Decodable {
let uuid : String? // can take null
let ip : String
let playerid : String
let connected : Int
let firmware : String
let displaytype : String
let power : Int
let name : String
let playerindex : String
let canpoweroff : Int
let isplayer : Int
let seq_no : Int
let isplaying : Int
let modelname : String
let model : String
}
然后在您的Result
枚举的可解码要求一致性中,您需要为您的PlayersLoop
结构添加另一个案例。
extension Result: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self = .string(string)
} else if let int = try? container.decode(Int.self) {
self = .int(int)
} else if let playersLoop = try? container.decode([PlayersLoop].self) {
self = .players_loop(playersLoop)
} else {
self = .array(try container.decode([Result].self))
}
}
}
现在,除了这里的问题之外,在为可解码要求提供自定义初始化程序时,您应该更加明确。一旦你使用try?
你就对编译器说
“我不在乎这是否失败”
所以你应该首先处理错误情况。如果你这样做了,你根本不会把这个问题作为一个问题发布。
推荐阅读
- python - 如何从一组固定键的值列表中制作字典列表
- spring-boot - spring-boot-starter-freemarker 找不到模板
- javascript - 变量未定义(错误)
- sql - 检索昨天 0700 和今天 0700 之间的数据
- r - 获取作为函数参数传递的变量名称
- c# - VS-Code 使用 .editorconfig/OmniSharp 抑制 C# 建议
- c# - EF Core:更新数据库错误:关系“所有者”已存在
- python - 如何从 python(arduino-python 串行通信)重新启动循环?
- c# - 快速操作属性 - 仅限吸气剂?
- android - MaterialButton 比应有的宽度更宽