Swift 如何给常量 Decodable 对象动态添加属性?
How to dynamically add properties to a constant Decodable object in Swift?
背景
基本上我 api returns 是这样的:
"order_detail": [
{
"id": 6938,
"order_id": 6404,
"item_price": "4",
..
"item": {
"id": 12644,
"ref": "Iced Caffe Americano",
"short_description": "",
..
在我的可解码对象中我得到了这个
public struct OrderDetail: Decodable {
public let id: Int
public let order_id: Int
public let item_price: String?
..
public let item: Item?
和
public struct Item: Decodable {
public var id: Int
public var ref: String?
public var short_description: String?
问题是在代码的其他地方,有一个方法期望 Item 对象具有 item_price
。
问题
我想做的是调整或改变这个常量 Item 对象并动态添加 item_price
属性 到它。我该怎么做?
解决方法,其他解决方案
1。更改 json
我知道同样的问题还有许多其他解决方案(我正在研究它,这只是修改 api 端点以满足我的需要)。但是这个选项还是并非总是可能(即假设后端团队是独立的)
2。改变函数期望值
这也是可能的,但也不便宜,因为此功能用于应用程序中我可能无法控制的许多其他地方
如果要将 属性 添加到不属于其 JSON 表示的 Decodable
类型,那么只需要声明一个 CodingKey
一致类型,并且省略特定的 属性 名称,以便自动合成的 init(from decoder:Decoder)
初始化程序知道不要在 JSON.
中查找该值
顺便说一句,您还应该遵守 Swift 命名约定(变量名小驼峰命名)并使用 CodingKey
将 JSON 键映射到 属性 名称。
public struct Item: Decodable {
public var id: Int
public var ref: String?
public var shortDescription: String?
public var itemPrice: String? // or whatever else its type needs to be
private enum CodingKeys: String, CodingKey {
case id, ref, shortDescription = "short_description"
}
}
这是实现此目的的一种方法
在OrderDetail解码中接管Item
的初始化。
struct OrderDetail: Decodable {
let id: Int
let orderId: Int
let itemPrice: String?
let item: Item
private enum OrderDetailCodingKey: CodingKey {
case id
case orderId
case itemPrice
case item
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: OrderDetailCodingKey.self)
self.id = try container.decode(Int.self, forKey: .id)
self.orderId = try container.decode(Int.self, forKey: .orderId)
let itemPrice = try container.decode(String?.self, forKey: .itemPrice)
self.itemPrice = itemPrice
self.item = try Item(from: decoder, itemPrice: itemPrice)
}
}
使用自定义初始化程序创建您的项目。
struct Item: Decodable {
let id: Int
let ref: String?
let shortDescription: String?
let itemPrice: String?
private enum ItemCodingKeys: CodingKey {
case id
case ref
case shortDescription
}
init(from decoder: Decoder, itemPrice: String?) throws {
let container = try decoder.container(keyedBy: ItemCodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.ref = try? container.decode(String.self, forKey: .ref)
self.shortDescription = try? container.decode(String.self, forKey: .shortDescription)
self.itemPrice = itemPrice
}
}
您可以调用以下函数来测试功能:
private func test() {
let json = """
{"id":6938,"orderId":6404,"itemPrice":"4","item":{"id":12644,"ref":"Iced Caffe Americano","shortDescription":""}}
"""
let data = json.data(using: .utf8)
let decoder = JSONDecoder()
if let data = data {
do {
let order = try decoder.decode(OrderDetail.self, from: data)
print(order)
} catch let jsonError {
os_log("JSON decoding failed [%@]", String(describing: jsonError))
}
} else {
os_log("No data found")
}
}
背景
基本上我 api returns 是这样的:
"order_detail": [
{
"id": 6938,
"order_id": 6404,
"item_price": "4",
..
"item": {
"id": 12644,
"ref": "Iced Caffe Americano",
"short_description": "",
..
在我的可解码对象中我得到了这个
public struct OrderDetail: Decodable {
public let id: Int
public let order_id: Int
public let item_price: String?
..
public let item: Item?
和
public struct Item: Decodable {
public var id: Int
public var ref: String?
public var short_description: String?
问题是在代码的其他地方,有一个方法期望 Item 对象具有 item_price
。
问题
我想做的是调整或改变这个常量 Item 对象并动态添加 item_price
属性 到它。我该怎么做?
解决方法,其他解决方案
1。更改 json
我知道同样的问题还有许多其他解决方案(我正在研究它,这只是修改 api 端点以满足我的需要)。但是这个选项还是并非总是可能(即假设后端团队是独立的)
2。改变函数期望值
这也是可能的,但也不便宜,因为此功能用于应用程序中我可能无法控制的许多其他地方
如果要将 属性 添加到不属于其 JSON 表示的 Decodable
类型,那么只需要声明一个 CodingKey
一致类型,并且省略特定的 属性 名称,以便自动合成的 init(from decoder:Decoder)
初始化程序知道不要在 JSON.
顺便说一句,您还应该遵守 Swift 命名约定(变量名小驼峰命名)并使用 CodingKey
将 JSON 键映射到 属性 名称。
public struct Item: Decodable {
public var id: Int
public var ref: String?
public var shortDescription: String?
public var itemPrice: String? // or whatever else its type needs to be
private enum CodingKeys: String, CodingKey {
case id, ref, shortDescription = "short_description"
}
}
这是实现此目的的一种方法
在OrderDetail解码中接管Item
的初始化。
struct OrderDetail: Decodable {
let id: Int
let orderId: Int
let itemPrice: String?
let item: Item
private enum OrderDetailCodingKey: CodingKey {
case id
case orderId
case itemPrice
case item
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: OrderDetailCodingKey.self)
self.id = try container.decode(Int.self, forKey: .id)
self.orderId = try container.decode(Int.self, forKey: .orderId)
let itemPrice = try container.decode(String?.self, forKey: .itemPrice)
self.itemPrice = itemPrice
self.item = try Item(from: decoder, itemPrice: itemPrice)
}
}
使用自定义初始化程序创建您的项目。
struct Item: Decodable {
let id: Int
let ref: String?
let shortDescription: String?
let itemPrice: String?
private enum ItemCodingKeys: CodingKey {
case id
case ref
case shortDescription
}
init(from decoder: Decoder, itemPrice: String?) throws {
let container = try decoder.container(keyedBy: ItemCodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.ref = try? container.decode(String.self, forKey: .ref)
self.shortDescription = try? container.decode(String.self, forKey: .shortDescription)
self.itemPrice = itemPrice
}
}
您可以调用以下函数来测试功能:
private func test() {
let json = """
{"id":6938,"orderId":6404,"itemPrice":"4","item":{"id":12644,"ref":"Iced Caffe Americano","shortDescription":""}}
"""
let data = json.data(using: .utf8)
let decoder = JSONDecoder()
if let data = data {
do {
let order = try decoder.decode(OrderDetail.self, from: data)
print(order)
} catch let jsonError {
os_log("JSON decoding failed [%@]", String(describing: jsonError))
}
} else {
os_log("No data found")
}
}