编码字典而不在 Swift 中添加编码键枚举
Encode dictionary without adding the coding key enum in Swift
我想编码一个 JSON 可以是
{"hw1":{"get_trouble":true},"seq":2,"session_id":1}
或
{"hw2":{"get_trouble":true},"seq":3,"session_id":2}
用于编码的 class 如下所示
class Request: Codable {
let sessionId, seq:Int
let content:[String:Content]
enum CodingKeys:String, CodingKey{
case sessionId = "session_id"
case seq
case content
}
init(sessionId:Int, seq:Int, content:[String:Content]) {
self.sessionId = sessionId
self.seq = seq
self.content = content
}
}
class Content:Codable{
let getTrouble = true
enum CodingKeys:String, CodingKey {
case getTrouble = "get_trouble"
}
}
如何对请求进行编码以便获得所需的结果?目前,如果我这样做
let request = Request(sessionId: session, seq: seq, content: [type:content])
let jsonData = try! encoder.encode(request)
我明白了
{"content":{"hw1":{"get_trouble":true}},"seq":2,"session_id":1}
而且我不希望 JSON 中有“内容”。已经调查
Swift Codable: encode structure with dynamic keys
并且无法弄清楚如何在我的用例中应用
与几乎所有自定义编码问题一样,您需要的工具是 AnyStringKey(这让我很沮丧,因为它不在 stdlib 中):
struct AnyStringKey: CodingKey, Hashable, ExpressibleByStringLiteral {
var stringValue: String
init(stringValue: String) { self.stringValue = stringValue }
init(_ stringValue: String) { self.init(stringValue: stringValue) }
var intValue: Int?
init?(intValue: Int) { return nil }
init(stringLiteral value: String) { self.init(value) }
}
这只是让您对任意键进行编码和编码。有了这个,编码器就很简单了:
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyStringKey.self)
for (key, value) in content {
try container.encode(value, forKey: AnyStringKey(key))
}
try container.encode(sessionId, forKey: AnyStringKey("session_id"))
try container.encode(seq, forKey: AnyStringKey("seq"))
}
这假定您的意思是在内容中允许多个 key/value 对。我希望你不会;您只是在使用字典,因为您想要一种更好的编码方式。如果 Content 有一个键,那么你可以这样更自然地重写它:
// Content only encodes getTrouble; it doesn't encode key
struct Content:Codable{
let key: String
let getTrouble: Bool
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(["get_trouble": getTrouble])
}
}
struct Request: Codable {
// ...
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyStringKey.self)
try container.encode(content, forKey: AnyStringKey(content.key))
try container.encode(sessionId, forKey: AnyStringKey("session_id"))
try container.encode(seq, forKey: AnyStringKey("seq"))
}
}
现在这可能仍然困扰您,因为它将部分内容编码逻辑推入了 Request。 (好吧,也许这只是困扰我。)如果你暂时搁置 Codable
,你也可以解决它。
// Encode Content directly into container
extension KeyedEncodingContainer where K == AnyStringKey {
mutating func encode(_ value: Content) throws {
try encode(["get_trouble": value.getTrouble], forKey: AnyStringKey(value.key))
}
}
struct Request: Codable {
// ...
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyStringKey.self)
// And encode into the container (note no "forKey")
try container.encode(content)
try container.encode(sessionId, forKey: AnyStringKey("session_id"))
try container.encode(seq, forKey: AnyStringKey("seq"))
}
}
我想编码一个 JSON 可以是
{"hw1":{"get_trouble":true},"seq":2,"session_id":1}
或
{"hw2":{"get_trouble":true},"seq":3,"session_id":2}
用于编码的 class 如下所示
class Request: Codable {
let sessionId, seq:Int
let content:[String:Content]
enum CodingKeys:String, CodingKey{
case sessionId = "session_id"
case seq
case content
}
init(sessionId:Int, seq:Int, content:[String:Content]) {
self.sessionId = sessionId
self.seq = seq
self.content = content
}
}
class Content:Codable{
let getTrouble = true
enum CodingKeys:String, CodingKey {
case getTrouble = "get_trouble"
}
}
如何对请求进行编码以便获得所需的结果?目前,如果我这样做
let request = Request(sessionId: session, seq: seq, content: [type:content])
let jsonData = try! encoder.encode(request)
我明白了
{"content":{"hw1":{"get_trouble":true}},"seq":2,"session_id":1}
而且我不希望 JSON 中有“内容”。已经调查
Swift Codable: encode structure with dynamic keys
并且无法弄清楚如何在我的用例中应用
与几乎所有自定义编码问题一样,您需要的工具是 AnyStringKey(这让我很沮丧,因为它不在 stdlib 中):
struct AnyStringKey: CodingKey, Hashable, ExpressibleByStringLiteral {
var stringValue: String
init(stringValue: String) { self.stringValue = stringValue }
init(_ stringValue: String) { self.init(stringValue: stringValue) }
var intValue: Int?
init?(intValue: Int) { return nil }
init(stringLiteral value: String) { self.init(value) }
}
这只是让您对任意键进行编码和编码。有了这个,编码器就很简单了:
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyStringKey.self)
for (key, value) in content {
try container.encode(value, forKey: AnyStringKey(key))
}
try container.encode(sessionId, forKey: AnyStringKey("session_id"))
try container.encode(seq, forKey: AnyStringKey("seq"))
}
这假定您的意思是在内容中允许多个 key/value 对。我希望你不会;您只是在使用字典,因为您想要一种更好的编码方式。如果 Content 有一个键,那么你可以这样更自然地重写它:
// Content only encodes getTrouble; it doesn't encode key
struct Content:Codable{
let key: String
let getTrouble: Bool
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(["get_trouble": getTrouble])
}
}
struct Request: Codable {
// ...
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyStringKey.self)
try container.encode(content, forKey: AnyStringKey(content.key))
try container.encode(sessionId, forKey: AnyStringKey("session_id"))
try container.encode(seq, forKey: AnyStringKey("seq"))
}
}
现在这可能仍然困扰您,因为它将部分内容编码逻辑推入了 Request。 (好吧,也许这只是困扰我。)如果你暂时搁置 Codable
,你也可以解决它。
// Encode Content directly into container
extension KeyedEncodingContainer where K == AnyStringKey {
mutating func encode(_ value: Content) throws {
try encode(["get_trouble": value.getTrouble], forKey: AnyStringKey(value.key))
}
}
struct Request: Codable {
// ...
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyStringKey.self)
// And encode into the container (note no "forKey")
try container.encode(content)
try container.encode(sessionId, forKey: AnyStringKey("session_id"))
try container.encode(seq, forKey: AnyStringKey("seq"))
}
}