zem*_*rco 13 ios swift codable
我有一些通过websocket连接进入的JSON消息.
// sample message
{
type: "person",
data: {
name: "john"
}
}
// some other message
{
type: "location",
data: {
x: 101,
y: 56
}
}
Run Code Online (Sandbox Code Playgroud)
如何使用Swift 4和Codable协议将这些消息转换为正确的结构?
在Go中我可以做类似的事情:"嘿,此刻我只关心这个type领域而我对其余部分(data部分)不感兴趣." 它看起来像这样
type Message struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到Data的那样json.RawMessage,可以在以后解析.这是一个完整的示例https://golang.org/pkg/encoding/json/#example_RawMessage_unmarshal.
我可以在Swift中做类似的事情吗?喜欢(尚未尝试过)
struct Message: Codable {
var type: String
var data: [String: Any]
}
Run Code Online (Sandbox Code Playgroud)
然后switch在type将字典转换为适当的结构.那会有用吗?
Rob*_*Rob 13
我不会依赖Dictionary.我会使用自定义类型.
例如,让我们假设:
你知道你要回来的对象(因为请求的性质); 和
这两种类型的响应真正返回相同的结构,除了内容data.
在这种情况下,您可以使用一个非常简单的通用模式:
struct Person: Decodable {
let name: String
}
struct Location: Decodable {
let x: Int
let y: Int
}
struct ServerResponse<T: Decodable>: Decodable {
let type: String
let data: T
}
Run Code Online (Sandbox Code Playgroud)
然后,当你想用a解析一个响应时Person,它将是:
let data = json.data(using: .utf8)!
do {
let responseObject = try JSONDecoder().decode(ServerResponse<Person>.self, from: data)
let person = responseObject.data
print(person)
} catch let parseError {
print(parseError)
}
Run Code Online (Sandbox Code Playgroud)
或解析一个Location:
do {
let responseObject = try JSONDecoder().decode(ServerResponse<Location>.self, from: data)
let location = responseObject.data
print(location)
} catch let parseError {
print(parseError)
}
Run Code Online (Sandbox Code Playgroud)
可以接受更复杂的模式(例如,data基于type它遇到的值动态解析类型),但除非必要,否则我不会倾向于追求这样的模式.这是一种很好的,简单的方法,它可以实现典型模式,您可以在其中了解特定请求的相关响应类型.
如果您希望可以type使用从data值中解析的值来验证值.考虑:
enum PayloadType: String, Decodable {
case person = "person"
case location = "location"
}
protocol Payload: Decodable {
static var payloadType: PayloadType { get }
}
struct Person: Payload {
let name: String
static let payloadType = PayloadType.person
}
struct Location: Payload {
let x: Int
let y: Int
static let payloadType = PayloadType.location
}
struct ServerResponse<T: Payload>: Decodable {
let type: PayloadType
let data: T
}
Run Code Online (Sandbox Code Playgroud)
然后,您的parse函数不仅可以解析正确的data结构,还可以确认该type值,例如:
enum ParseError: Error {
case wrongPayloadType
}
func parse<T: Payload>(_ data: Data) throws -> T {
let responseObject = try JSONDecoder().decode(ServerResponse<T>.self, from: data)
guard responseObject.type == T.payloadType else {
throw ParseError.wrongPayloadType
}
return responseObject.data
}
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样称呼它:
do {
let location: Location = try parse(data)
print(location)
} catch let parseError {
print(parseError)
}
Run Code Online (Sandbox Code Playgroud)
这不仅返回Location对象,还验证type服务器响应中的值.我不确定这是值得的,但如果你想这样做,那就是一种方法.
如果您在处理JSON时确实不知道类型,那么您只需编写一个init(coder:)首先解析的类型type,然后data根据type包含的值解析它:
enum PayloadType: String, Decodable {
case person = "person"
case location = "location"
}
protocol Payload: Decodable {
static var payloadType: PayloadType { get }
}
struct Person: Payload {
let name: String
static let payloadType = PayloadType.person
}
struct Location: Payload {
let x: Int
let y: Int
static let payloadType = PayloadType.location
}
struct ServerResponse: Decodable {
let type: PayloadType
let data: Payload
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
type = try values.decode(PayloadType.self, forKey: .type)
switch type {
case .person:
data = try values.decode(Person.self, forKey: .data)
case .location:
data = try values.decode(Location.self, forKey: .data)
}
}
enum CodingKeys: String, CodingKey {
case type, data
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以做以下事情:
do {
let responseObject = try JSONDecoder().decode(ServerResponse.self, from: data)
let payload = responseObject.data
if payload is Location {
print("location:", payload)
} else if payload is Person {
print("person:", payload)
}
} catch let parseError {
print(parseError)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2487 次 |
| 最近记录: |