使用 Codable 使用动态编码密钥解码 json?
Decode json with dynamic coding key using Codable?
示例 Json 我需要解码。在 "text" 键中,我们有 [String: String] 字典。元素的数量在 "count" 中。我应该如何正确解码?
{
"name": "LoremIpsum",
"index": "1",
"text": {
"text_1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"text_2": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
"text_3": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ",
"text_4": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
},
"count": "4"
}
我的 Codable 模型:
class Text: Codable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String?
public var index: Int?
public var count: Int?
public var texts: [String]?
init() {
name = ""
index = 0
count = 0
texts = []
}
init(name: String,
index: Int,
count: Int,
texts: [String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var text = container.nestedContainer(keyedBy: CodingKeys.self, forKey: . text)
} <---- also why do I need this method?
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
for i in (1...self.count!) {
self.texts!.append(try text.decode(String.self, forKey: Text.CodingKeys.init(rawValue: "text_\(i)") ?? .text))
}
}
}
然后我解码它:
if let path = Bundle.main.path(forResource: "en_translation_001", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
print("jsonData:\(jsonObj)")
} catch let error {
print("parse error: \(error.localizedDescription)")
}
} else {
print("Invalid filename/path.")
}
但是我遇到了解析错误
parse error: The data couldn’t be read because it is missing.
我的代码有什么问题?解码这种动态编码密钥是一种好方法吗?
修复JSON
我想您希望索引和计数是数字。所以替换这个 "index": "1"
和这个 "count": "4"
有了这个 "index": 1
和这个 "count": 4
Class结构
使用 Codable 协议,任何这些 CodingKeys 和编码函数或所需的初始化都不是必需的。同时将 texts 属性 数据格式更改为 [String: String]
所以,像这样替换你的 class:
class Text: Codable {
public var name: String
public var index: Int
public var count: Int
public var texts: [String: String]
init(name: String, index: Int, count: Int, texts: [String: String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
}
解码
解码你的 json 使用你上面写的,这是正确的
let object = try JSONDecoder().decode(Text.self, from: data)
你需要
struct Root: Codable {
let name, index,count: String
let text: [String:String]
}
--
let res = try? JSONDecoder().decode(Root.self,from:jsonData)
你最需要的是这个:
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: [=10=]) }
这会将字典转换为值数组。虽然原则上 JSON 值未排序,但 allKeys
已排序(因为它是序列化协议)。
如果您只符合 Decodable
而不是 Codable
,则不需要编码方法。另外,你的大部分可选值都不是真正的可选值。
把这些东西放在一起,你会得到这个:
class Text: Decodable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String
public var index: Int
public var count: Int
public var texts: [String]
init(name: String = "",
index: Int = 0,
count: Int = 0,
texts: [String] = []) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: [=11=]) }
}
}
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
示例 Json 我需要解码。在 "text" 键中,我们有 [String: String] 字典。元素的数量在 "count" 中。我应该如何正确解码?
{
"name": "LoremIpsum",
"index": "1",
"text": {
"text_1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"text_2": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
"text_3": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ",
"text_4": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
},
"count": "4"
}
我的 Codable 模型:
class Text: Codable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String?
public var index: Int?
public var count: Int?
public var texts: [String]?
init() {
name = ""
index = 0
count = 0
texts = []
}
init(name: String,
index: Int,
count: Int,
texts: [String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var text = container.nestedContainer(keyedBy: CodingKeys.self, forKey: . text)
} <---- also why do I need this method?
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
for i in (1...self.count!) {
self.texts!.append(try text.decode(String.self, forKey: Text.CodingKeys.init(rawValue: "text_\(i)") ?? .text))
}
}
}
然后我解码它:
if let path = Bundle.main.path(forResource: "en_translation_001", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
print("jsonData:\(jsonObj)")
} catch let error {
print("parse error: \(error.localizedDescription)")
}
} else {
print("Invalid filename/path.")
}
但是我遇到了解析错误
parse error: The data couldn’t be read because it is missing.
我的代码有什么问题?解码这种动态编码密钥是一种好方法吗?
修复JSON
我想您希望索引和计数是数字。所以替换这个 "index": "1"
和这个 "count": "4"
有了这个 "index": 1
和这个 "count": 4
Class结构
使用 Codable 协议,任何这些 CodingKeys 和编码函数或所需的初始化都不是必需的。同时将 texts 属性 数据格式更改为 [String: String]
所以,像这样替换你的 class:
class Text: Codable {
public var name: String
public var index: Int
public var count: Int
public var texts: [String: String]
init(name: String, index: Int, count: Int, texts: [String: String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
}
解码
解码你的 json 使用你上面写的,这是正确的
let object = try JSONDecoder().decode(Text.self, from: data)
你需要
struct Root: Codable {
let name, index,count: String
let text: [String:String]
}
--
let res = try? JSONDecoder().decode(Root.self,from:jsonData)
你最需要的是这个:
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: [=10=]) }
这会将字典转换为值数组。虽然原则上 JSON 值未排序,但 allKeys
已排序(因为它是序列化协议)。
如果您只符合 Decodable
而不是 Codable
,则不需要编码方法。另外,你的大部分可选值都不是真正的可选值。
把这些东西放在一起,你会得到这个:
class Text: Decodable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String
public var index: Int
public var count: Int
public var texts: [String]
init(name: String = "",
index: Int = 0,
count: Int = 0,
texts: [String] = []) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: [=11=]) }
}
}
let jsonObj = try JSONDecoder().decode(Text.self, from: data)