json - 创建动态可编码对象
问题描述
我正在创建一个具有动态内容的消息对象。我设法让它工作,但我的解决方案似乎容易出错并且有很多样板。
进行更改,例如:添加新的动态内容类型或添加删除字段似乎需要很多步骤。
想知道是否有更好的解决方案来解决我的问题?
我的消息可能如下所示:
{
"sentAt" : 1587022227,
"canBeDeletedForAllUsers" : true,
"content" : {
"memberUserIDs" : [
"7835BB24-2880-49E0-AEB1-8FEE74FE6569",
"478D6BDD-4921-4166-8ED2-9FB6194450E6",
"8D0B0684-EDE9-47C8-8318-D96BE91E0879"
]
},
"id" : "9D4A2D5E-F316-43D7-880E-D6793A11F9C8",
"senderUserID" : "71F1CDB9-D4D0-4C99-8B87-8E09A06259C8",
"channelID" : "group-241B1EED-3704-452D-8D9F-24F75E2AB76E",
"canBeDeletedOnlyForSelf" : true,
"canBeForwarded" : true,
"views" : 0,
"type" : "addMembers",
"canBeEdited" : true
}
或者
{
"sentAt" : 1587023808,
"canBeDeletedForAllUsers" : true,
"content" : {
"text" : "hello"
},
"id" : "3DA4810C-C6FF-456E-8431-A61674BF6967",
"senderUserID" : "30FD4396-CDD8-47BD-AD26-896F6F33AC9F",
"channelID" : "group-31EA8B8B-B39D-4B49-9D8C-F66D3E11A8F5",
"canBeDeletedOnlyForSelf" : true,
"canBeForwarded" : true,
"views" : 0,
"type" : "text",
"canBeEdited" : true
}
并且可以根据我要添加的更多内容而变化更多。
这是我的解决方案:
public enum MessageContentType: String, Codable {
case addMembers
case text
}
public protocol MessageContent {}
public struct Message: Encodable {
let id: String
let senderUserID: String
let channelID: String
let canBeEdited: Bool
let canBeForwarded: Bool
let canBeDeletedOnlyForSelf: Bool
let canBeDeletedForAllUsers: Bool
let views: Int
let content: MessageContent
var sentAt: Int64
var type: MessageContentType
enum CodingKeys: CodingKey {
case id
case senderUserID
case channelID
case canBeEdited
case canBeForwarded
case canBeDeletedOnlyForSelf
case canBeDeletedForAllUsers
case views
case content
case sentAt
case type
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(senderUserID, forKey: .senderUserID)
try container.encode(channelID, forKey: .channelID)
try container.encode(canBeEdited, forKey: .canBeEdited)
try container.encode(canBeForwarded, forKey: .canBeForwarded)
try container.encode(canBeDeletedOnlyForSelf, forKey: .canBeDeletedOnlyForSelf)
try container.encode(canBeDeletedForAllUsers, forKey: .canBeDeletedForAllUsers)
try container.encode(views, forKey: .views)
try container.encode(sentAt, forKey: .sentAt)
try container.encode(type, forKey: .type)
if let content = content as? MessageChatAddMembers {
try container.encode(content, forKey: .content)
} else if let content = content as? MessageText {
try container.encode(content, forKey: .content)
} else {
let context = DecodingError.Context(codingPath: encoder.codingPath, debugDescription: "Invalid content!")
throw DecodingError.dataCorrupted(context)
}
}
}
extension Message: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
senderUserID = try container.decode(String.self, forKey: .senderUserID)
channelID = try container.decode(String.self, forKey: .channelID)
canBeEdited = try container.decode(Bool.self, forKey: .canBeEdited)
canBeForwarded = try container.decode(Bool.self, forKey: .canBeForwarded)
canBeDeletedOnlyForSelf = try container.decode(Bool.self, forKey: .canBeDeletedOnlyForSelf)
canBeDeletedForAllUsers = try container.decode(Bool.self, forKey: .canBeDeletedForAllUsers)
views = try container.decode(Int.self, forKey: .views)
sentAt = try container.decode(Int64.self, forKey: .sentAt)
type = try container.decode(MessageContentType.self, forKey: .type)
switch type {
case .addMembers:
content = try container.decode(MessageChatAddMembers.self, forKey: .content)
case .text:
content = try container.decode(MessageText.self, forKey: .content)
}
}
}
public struct MessageChatAddMembers: Codable, MessageContent {
let memberUserIDs: [String]
init() {
memberUserIDs = [UUID().uuidString, UUID().uuidString, UUID().uuidString]
}
}
public struct MessageText: Codable, MessageContent {
let text: String
init() {
text = "hello"
}
}
解决方案
如果您将type
字段移动到对象,那么您可以简单地为您的数据content
使用枚举。content
{
"sentAt" : 1587023808,
"canBeDeletedForAllUsers" : true,
"content" : {
"type" : "text",
"text" : "hello"
},
"id" : "3DA4810C-C6FF-456E-8431-A61674BF6967",
"senderUserID" : "30FD4396-CDD8-47BD-AD26-896F6F33AC9F",
"channelID" : "group-31EA8B8B-B39D-4B49-9D8C-F66D3E11A8F5",
"canBeDeletedOnlyForSelf" : true,
"canBeForwarded" : true,
"views" : 0,
"canBeEdited" : true
}
这仍然需要一些工作,但你MessageContent
会看起来像这样:
struct MessageContent: Codable {
enum CodingKeys: String, CodingKey {
case type
case text
case memberUserIDs
}
enum Data {
case text(String)
case memberUserIDs([String])
case unknown
}
let data: Data
init(data: Data) {
self.data = data
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
switch type {
case "text":
let text = try container.decode(String.self, forKey: .text)
self.data = .text(text)
case "memberUserIDs":
let memberUserIDs = try container.decode([String].self, forKey: .memberUserIDs)
self.data = .memberUserIDs(memberUserIDs)
default:
self.data = .unknown
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch data {
case .text(let text):
try container.encode(text, forKey: .text)
try container.encode("text", forKey: .type)
case .memberUserIDs(let memberUserIDs):
try container.encode(memberUserIDs, forKey: .memberUserIDs)
try container.encode("memberUserIDs", forKey: .type)
case .unknown:
break
}
}
}
Message
假设您的 JSON 中的字段与您可以摆脱的属性CodingKeys
和Codable
协议方法具有相同的名称。
public struct Message: Codable {
let id: String
let senderUserID: String
let channelID: String
let canBeEdited: Bool
let canBeForwarded: Bool
let canBeDeletedOnlyForSelf: Bool
let canBeDeletedForAllUsers: Bool
let views: Int
let content: MessageContent
var sentAt: Int64
}
推荐阅读
- java - 图的高效junit测试?
- c - C Programming Malloc 做得不对
- python - 使用 Python 从嵌入式谷歌地图中抓取点信息
- php - PHP - 人名的正则表达式(姓氏,名字)
- css - React JS react-bootstrap PopOver 使用 className 更改不同按钮的背景颜色
- android - Google Fit api 不跟踪数据
- postgresql - elixir,ecto:无法将参数传递给“CREATE VIEW”原始 SQL 查询
- c# - 将特定文件夹中的所有类标记为已过时
- google-cloud-platform - 在 google dialogflow 上使用单个服务帐户访问多个项目
- java - 如何从具有公共字段的另一个对象列表中为一个对象列表的字段设置值?