gpt4 book ai didi

ios - 如何使用 Swift Codable 处理部分动态的 JSON?

转载 作者:IT王子 更新时间:2023-10-29 05:23:50 24 4
gpt4 key购买 nike

我收到了一些通过 websocket 连接传入的 JSON 消息。

// sample message
{
type: "person",
data: {
name: "john"
}
}

// some other message
{
type: "location",
data: {
x: 101,
y: 56
}
}

如何使用 Swift 4 和 Codable 协议(protocol)将这些消息转换为适当的结构?

在 Go 中,我可以做类似的事情:“嘿,目前我只关心 type 字段,我对其余部分不感兴趣(data 部分)。”看起来像这样

type Message struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}

如您所见,Datajson.RawMessage 类型,稍后可以对其进行解析。这是一个完整的例子 https://golang.org/pkg/encoding/json/#example_RawMessage_unmarshal .

我可以在 Swift 中做类似的事情吗?喜欢(还没试过)

struct Message: Codable {
var type: String
var data: [String: Any]
}

然后 switch type 将字典转换为适当的结构。那行得通吗?

最佳答案

我不会依赖字典。我会使用自定义类型。

例如,让我们假设:

  • 您知道要取回哪个对象(由于请求的性质);和

  • 除了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
}

然后,当您想要解析带有 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)
}

或者解析一个Location:

do {
let responseObject = try JSONDecoder().decode(ServerResponse<Location>.self, from: data)

let location = responseObject.data
print(location)
} catch let parseError {
print(parseError)
}

人们可以接受更复杂的模式(例如,根据遇到的 type 值动态解析 data 类型),但我不倾向于除非必要,否则不要追求这样的模式。这是一种很好的、​​简单的方法,可以实现您知道特定请求的关联响应类型的典型模式。


如果您愿意,可以使用从 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<T: Payload>: Decodable {
let type: PayloadType
let data: T
}

然后,您的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
}

然后你可以这样调用它:

do {
let location: Location = try parse(data)
print(location)
} catch let parseError {
print(parseError)
}

这不仅会返回 Location 对象,还会验证服务器响应中 type 的值。我不确定是否值得付出努力,但如果您想这样做,这是一种方法。


如果在处理JSON的时候确实不知道类型,那么只需要写一个init(coder:)先解析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: 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
}

}

然后您可以执行以下操作:

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)
}

关于ios - 如何使用 Swift Codable 处理部分动态的 JSON?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48007761/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com