Need more help with json, any suggestions on how to lay this out?
Should players_loop be added in where I put it, etc... been going in circles for awhile.
struct:
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)!
test:
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)
}
Failed to decode: 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: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
Params section works great and is in the gist, answered here: Swift 4.1 Codable/Decodable Nested Array
So I used Params as the starting point for "Results" --> but now trying to add in players_loop.
Thanks!!!
https://gist.github.com/sckramer/5b90d3b15eef2c19196abab2d7698e70
You have several issues. First of all, you need to be aware of the optional
fields. From your attached JSON, it's clearly seen that uuid
inside players_loop
can be null. So this field should be optional as well in your model.
I purposefully changed the name of the struct to reflect Swift identifier convention (Type's name should start with 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
}
Then in the decodable requirement conformance of your Result
enum you need to add another case for your PlayersLoop
structure.
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))
}
}
}
Now, aside from your issues here, you should be more explicit when providing custom initializer for decodable requirement. As soon as you use try?
you are saying to the compiler
"I don't care if this fails"
So you should be handling the error cases as well in the first place. If you did so, you wouldn't be in the position of posting this issue as a question at all.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.