如何将 JSON 解码和编码为 Swift 中的通用结构
How do I decode and encode JSON into a generic struct in Swift
假设 JSON 看起来像这样:
{
"users": [
{
"id": 6,
"email": "123@gmail.com"
},
{
"id": 2,
"email": "345@gmail.com"
}
],
"meta": {
"current_page": 1,
"next_page": 2,
"prev_page": null,
"total_pages": 3,
"total_count": 12
}
}
有时它看起来像这样
{
"messages": [
{
"id": 6,
"text": "hello"
},
{
"id": 2,
"text": "hi"
}
],
"meta": {
"current_page": 1,
"next_page": 2,
"prev_page": null,
"total_pages": 3,
"total_count": 12
}
}
如您所见,编码键会根据JSON中的对象而变化。
我如何将此 JSON 解析为我可以阅读的动态内容,例如:
struct GenericListModel<ListObject: Codable>: Codable {
let list: [ListObject]
let page: PaginationModel
}
我将在其中单独创建 ListObject,例如:UserModel。
然后我将创建模型:
GenericListModel<UserModel>(list: UserModel(id: 6, email: "123@gmail.com"), page: PaginationModel())
您可能想稍微调整一下,但您可以这样做:
struct Metadata: Codable {
// ...
}
struct User: Codable {
let id: Int
let email: String
}
struct Message: Codable {
// ...
}
protocol ListKeyable: CodingKey {
static var listKey: Self { get }
}
enum UserKeys: String, ListKeyable {
case users
static var listKey: UserKeys { .users }
}
enum MessageKeys: String, ListKeyable {
case messages
static var listKey: MessageKeys { .messages }
}
class PagedList<Element: Codable, ListKeys: ListKeyable>: Codable {
enum MetaKeys: String, CodingKey {
case meta
}
let list: [Element]
let meta: Metadata
func encode(to encoder: Encoder) throws {
var container1 = encoder.container(keyedBy: ListKeys.self)
var container2 = encoder.container(keyedBy: MetaKeys.self)
try container1.encode(list, forKey: ListKeys.listKey)
try container2.encode(meta, forKey: .meta)
}
required init(from decoder: Decoder) throws {
let container1 = try decoder.container(keyedBy: ListKeys.self)
let container2 = try decoder.container(keyedBy: MetaKeys.self)
list = try container1.decode([Element].self, forKey: ListKeys.listKey)
meta = try container2.decode(Metadata.self, forKey: .meta)
}
}
let decoder = JSONDecoder()
let list = try decoder.decode(PagedList<User, UserKeys>.self, from: users)
假设 JSON 看起来像这样:
{
"users": [
{
"id": 6,
"email": "123@gmail.com"
},
{
"id": 2,
"email": "345@gmail.com"
}
],
"meta": {
"current_page": 1,
"next_page": 2,
"prev_page": null,
"total_pages": 3,
"total_count": 12
}
}
有时它看起来像这样
{
"messages": [
{
"id": 6,
"text": "hello"
},
{
"id": 2,
"text": "hi"
}
],
"meta": {
"current_page": 1,
"next_page": 2,
"prev_page": null,
"total_pages": 3,
"total_count": 12
}
}
如您所见,编码键会根据JSON中的对象而变化。 我如何将此 JSON 解析为我可以阅读的动态内容,例如:
struct GenericListModel<ListObject: Codable>: Codable {
let list: [ListObject]
let page: PaginationModel
}
我将在其中单独创建 ListObject,例如:UserModel。
然后我将创建模型:
GenericListModel<UserModel>(list: UserModel(id: 6, email: "123@gmail.com"), page: PaginationModel())
您可能想稍微调整一下,但您可以这样做:
struct Metadata: Codable {
// ...
}
struct User: Codable {
let id: Int
let email: String
}
struct Message: Codable {
// ...
}
protocol ListKeyable: CodingKey {
static var listKey: Self { get }
}
enum UserKeys: String, ListKeyable {
case users
static var listKey: UserKeys { .users }
}
enum MessageKeys: String, ListKeyable {
case messages
static var listKey: MessageKeys { .messages }
}
class PagedList<Element: Codable, ListKeys: ListKeyable>: Codable {
enum MetaKeys: String, CodingKey {
case meta
}
let list: [Element]
let meta: Metadata
func encode(to encoder: Encoder) throws {
var container1 = encoder.container(keyedBy: ListKeys.self)
var container2 = encoder.container(keyedBy: MetaKeys.self)
try container1.encode(list, forKey: ListKeys.listKey)
try container2.encode(meta, forKey: .meta)
}
required init(from decoder: Decoder) throws {
let container1 = try decoder.container(keyedBy: ListKeys.self)
let container2 = try decoder.container(keyedBy: MetaKeys.self)
list = try container1.decode([Element].self, forKey: ListKeys.listKey)
meta = try container2.decode(Metadata.self, forKey: .meta)
}
}
let decoder = JSONDecoder()
let list = try decoder.decode(PagedList<User, UserKeys>.self, from: users)