Swift 4.1 Codable/Decodable 嵌套数组
Swift 4.1 Codable/Decodable Nested Array
需要一些更复杂的帮助 json,最新的 swift4.1 encoder/decoder:
结构:
struct LMSRequest: Decodable {
let id : Int?
let method : String?
let params : [String]?
enum CodingKeys: String, CodingKey {
case id = "id"
case method = "method"
case params = "params"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decodeIfPresent(Int.self, forKey: .id)
method = try values.decodeIfPresent(String.self, forKey: .method)
params = try values.decodeIfPresent([String].self, forKey: .params)
}}
json:
let json = """
{
"id": 1,
"method": "slim.request",
"params": [
"b8:27:eb:db:6d:62",
[
"serverstatus",
"-",
1,
"tags:GPASIediqtymkovrfijnCYXRTIuwxNlasc"
]
]
}
""".data(using: .utf8)!
代码:
let decoder = JSONDecoder()
let lms = try decoder.decode(LMSRequest.self, from: json)
print(lms)
解码字符串时预计会出错,但却找到了数组。它来自 "params" 数组中的嵌套数组...真的很困惑如何构建它,谢谢!
根据您的描述,您应该将参数存储为这样的枚举:
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))
}
}
}
鉴于此,LMSRequest 中无需自定义解码逻辑:
struct LMSRequest: Decodable {
let id : Int?
let method : String?
let params : [Param]?
}
附带说明一下,我会仔细考虑这些字段是否都是可选的。 id
是可选的非常令人惊讶,method
是可选的非常令人惊讶,params
是可选的有点令人惊讶。如果它们不是真正可选的,请不要在类型中将它们设为可选。
根据您的评论,您可能误解了如何访问枚举。 params[1]
不是 [Param]
。这是一个 .array([Param])
。所以你必须对其进行模式匹配,因为它可能是一个字符串或一个整数。
if case let .array(values) = lms.params[1] { print(values[0]) }
就是说,如果您经常这样做,您可以通过 Param 的扩展来简化此操作:
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]
}
}
有了它,你可以这样说:
let serverstatus: String? = lms.params[1][0]?.stringValue
这可能更接近您的想法。 (: String?
只是为了清楚返回的类型;它不是必需的。)
有关此方法的更复杂和更完善的示例,请参阅我的 generic JSON Decodable 这是的子集。
需要一些更复杂的帮助 json,最新的 swift4.1 encoder/decoder:
结构:
struct LMSRequest: Decodable {
let id : Int?
let method : String?
let params : [String]?
enum CodingKeys: String, CodingKey {
case id = "id"
case method = "method"
case params = "params"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decodeIfPresent(Int.self, forKey: .id)
method = try values.decodeIfPresent(String.self, forKey: .method)
params = try values.decodeIfPresent([String].self, forKey: .params)
}}
json:
let json = """
{
"id": 1,
"method": "slim.request",
"params": [
"b8:27:eb:db:6d:62",
[
"serverstatus",
"-",
1,
"tags:GPASIediqtymkovrfijnCYXRTIuwxNlasc"
]
]
}
""".data(using: .utf8)!
代码:
let decoder = JSONDecoder()
let lms = try decoder.decode(LMSRequest.self, from: json)
print(lms)
解码字符串时预计会出错,但却找到了数组。它来自 "params" 数组中的嵌套数组...真的很困惑如何构建它,谢谢!
根据您的描述,您应该将参数存储为这样的枚举:
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))
}
}
}
鉴于此,LMSRequest 中无需自定义解码逻辑:
struct LMSRequest: Decodable {
let id : Int?
let method : String?
let params : [Param]?
}
附带说明一下,我会仔细考虑这些字段是否都是可选的。 id
是可选的非常令人惊讶,method
是可选的非常令人惊讶,params
是可选的有点令人惊讶。如果它们不是真正可选的,请不要在类型中将它们设为可选。
根据您的评论,您可能误解了如何访问枚举。 params[1]
不是 [Param]
。这是一个 .array([Param])
。所以你必须对其进行模式匹配,因为它可能是一个字符串或一个整数。
if case let .array(values) = lms.params[1] { print(values[0]) }
就是说,如果您经常这样做,您可以通过 Param 的扩展来简化此操作:
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]
}
}
有了它,你可以这样说:
let serverstatus: String? = lms.params[1][0]?.stringValue
这可能更接近您的想法。 (: String?
只是为了清楚返回的类型;它不是必需的。)
有关此方法的更复杂和更完善的示例,请参阅我的 generic JSON Decodable 这是的子集。