如何 json 解码链接的相关项目?
How to json decode linked, related items?
让我们假设这个 json 表示多语言单词:
[{
"id": "en_cat",
"name": "cat",
"def": "A cat is a domestic animal of the feline family.",
"trans": {
"fr": "fr_chat",
"ru": "ru_ко́шка"
}
}, {
"id": "fr_chat",
"name": "chat",
"def": "Le chat est un animal domestique de la famille des félins.",
"trans": {
"en": "en_cat",
"ru": "ru_ко́шка"
}
}, {
"id": "ru_ко́шка",
"name": "ко́шка",
"def": "..."
"trans": {
"en": "en_cat",
"fr": "fr_chat"
}
}]
此 json 在 "trans"(翻译)嵌套容器中有相互关联的项目。
我的class很直接
class Word: Decodable {
var id: String
var name: String
var definition: String
var enTranslation: Word?
var frTranslation: Word?
var ruTranslation: Word?
enum JsonCodingKey: String, CodingKey {
case id
case name
case def
case trans
}
enum JsonTransCodingKey: String, CodingKey {
case en
case fr
case ru
}
convenience init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: JsonCodingKey.self)
let id = try container.decode(String.self, forKey: .id)
let name = try container.decode(String.self, forKey: .name)
let definition = try container.decode(String.self, forKey: .def)
self.init(id: id, name: name, definition: definition)
// Tricky part here...
let transContainer = try container.nestedContainer(keyedBy: JsonTransCodingKey.self, forKey: .trans)
if let en = transContainer.decode(String.self, forKey: .en) {
self.enTranslation = realm.find(wordId: en) // Singleton that looks into memory for the word
}
// And repeat the same if logic for the other languages...
}
}
JSON 解码最快(CPU)的方法是什么?
我的处理方式"feels"错误:
- 我使用
解码单词
let jsonDecoder = JSONDecoder()
let words = jsonDecoder.decode([Word].self, from: data)
但是这些词没有任何翻译链接,因为它们在实时解析期间不是"known"。
在我的示例中,当我们解析第一个单词 "cat" 时,我们仍然不知道法语和俄语单词。
- 所以我必须再次解码,一旦我记住了所有的单词。
let jsonDecoder = JSONDecoder()
let words = jsonDecoder.decode([Word].self, from: data) // Words don't have their translations
self.saveInMemory(words) // In my case, it is saved to Realm.
let words = jsonDecoder.decode([Word].self, from: data)
/* Words are now linked to each others
Because during decoding, the func Word.init(from decoder) will
look into `Realm` and find the translations. */
这种双重解码感觉有点矫枉过正。反正直接搜索json数据不行吗?
先解码,再生成结构。您正在尝试将这两者结合起来,这是没有意义的。
您的第一次解码执行实际解码,您的第二次解码仅执行 linking。
取而代之的是,解码为临时结构,构建标识符字典并将其用于link最终对象。
老实说,没有必要做实际的 linking。它仍然可以是完全动态的,使用字典。
一种可能的方法:
let data = """
[{
"id": "en_cat",
"name": "cat",
"def": "A cat is a domestic animal of the feline family.",
"trans": {
"fr": "fr_chat",
"ru": "ru_ко́шка"
}
}, {
"id": "fr_chat",
"name": "chat",
"def": "Le chat est un animal domestique de la famille des félins.",
"trans": {
"en": "en_cat",
"ru": "ru_ко́шка"
}
}, {
"id": "ru_ко́шка",
"name": "ко́шка",
"def": "...",
"trans": {
"en": "en_cat",
"fr": "fr_chat"
}
}]
""".data(using: .utf8)!
enum Language: String {
case english = "en"
case french = "fr"
case russian = "ru"
}
class Word: Decodable {
let id: String
let name: String
let definition: String
let translationsIds: [String: String]
weak var parentDictionary: Dictionary!
private enum CodingKeys: String, CodingKey {
case id
case name
case definition = "def"
case translationsIds = "trans"
}
func translation(for language: Language) -> Word? {
return translationsIds[language.rawValue].flatMap { parentDictionary.words[[=10=]] }
}
}
class Dictionary: Decodable {
let words: [String: Word]
required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let words = try container.decode([Word].self)
self.words = [String: Word](uniqueKeysWithValues: words.map { (key: [=10=].id, value: [=10=]) })
for word in words {
word.parentDictionary = self
}
}
}
let decoder = JSONDecoder()
let dictionary = try decoder.decode(Dictionary.self, from: data)
print(dictionary.words["fr_chat"]?.translation(for: .english)?.name)
让我们假设这个 json 表示多语言单词:
[{
"id": "en_cat",
"name": "cat",
"def": "A cat is a domestic animal of the feline family.",
"trans": {
"fr": "fr_chat",
"ru": "ru_ко́шка"
}
}, {
"id": "fr_chat",
"name": "chat",
"def": "Le chat est un animal domestique de la famille des félins.",
"trans": {
"en": "en_cat",
"ru": "ru_ко́шка"
}
}, {
"id": "ru_ко́шка",
"name": "ко́шка",
"def": "..."
"trans": {
"en": "en_cat",
"fr": "fr_chat"
}
}]
此 json 在 "trans"(翻译)嵌套容器中有相互关联的项目。
我的class很直接
class Word: Decodable {
var id: String
var name: String
var definition: String
var enTranslation: Word?
var frTranslation: Word?
var ruTranslation: Word?
enum JsonCodingKey: String, CodingKey {
case id
case name
case def
case trans
}
enum JsonTransCodingKey: String, CodingKey {
case en
case fr
case ru
}
convenience init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: JsonCodingKey.self)
let id = try container.decode(String.self, forKey: .id)
let name = try container.decode(String.self, forKey: .name)
let definition = try container.decode(String.self, forKey: .def)
self.init(id: id, name: name, definition: definition)
// Tricky part here...
let transContainer = try container.nestedContainer(keyedBy: JsonTransCodingKey.self, forKey: .trans)
if let en = transContainer.decode(String.self, forKey: .en) {
self.enTranslation = realm.find(wordId: en) // Singleton that looks into memory for the word
}
// And repeat the same if logic for the other languages...
}
}
JSON 解码最快(CPU)的方法是什么?
我的处理方式"feels"错误:
- 我使用 解码单词
let jsonDecoder = JSONDecoder()
let words = jsonDecoder.decode([Word].self, from: data)
但是这些词没有任何翻译链接,因为它们在实时解析期间不是"known"。
在我的示例中,当我们解析第一个单词 "cat" 时,我们仍然不知道法语和俄语单词。
- 所以我必须再次解码,一旦我记住了所有的单词。
let jsonDecoder = JSONDecoder()
let words = jsonDecoder.decode([Word].self, from: data) // Words don't have their translations
self.saveInMemory(words) // In my case, it is saved to Realm.
let words = jsonDecoder.decode([Word].self, from: data)
/* Words are now linked to each others
Because during decoding, the func Word.init(from decoder) will
look into `Realm` and find the translations. */
这种双重解码感觉有点矫枉过正。反正直接搜索json数据不行吗?
先解码,再生成结构。您正在尝试将这两者结合起来,这是没有意义的。
您的第一次解码执行实际解码,您的第二次解码仅执行 linking。
取而代之的是,解码为临时结构,构建标识符字典并将其用于link最终对象。
老实说,没有必要做实际的 linking。它仍然可以是完全动态的,使用字典。
一种可能的方法:
let data = """
[{
"id": "en_cat",
"name": "cat",
"def": "A cat is a domestic animal of the feline family.",
"trans": {
"fr": "fr_chat",
"ru": "ru_ко́шка"
}
}, {
"id": "fr_chat",
"name": "chat",
"def": "Le chat est un animal domestique de la famille des félins.",
"trans": {
"en": "en_cat",
"ru": "ru_ко́шка"
}
}, {
"id": "ru_ко́шка",
"name": "ко́шка",
"def": "...",
"trans": {
"en": "en_cat",
"fr": "fr_chat"
}
}]
""".data(using: .utf8)!
enum Language: String {
case english = "en"
case french = "fr"
case russian = "ru"
}
class Word: Decodable {
let id: String
let name: String
let definition: String
let translationsIds: [String: String]
weak var parentDictionary: Dictionary!
private enum CodingKeys: String, CodingKey {
case id
case name
case definition = "def"
case translationsIds = "trans"
}
func translation(for language: Language) -> Word? {
return translationsIds[language.rawValue].flatMap { parentDictionary.words[[=10=]] }
}
}
class Dictionary: Decodable {
let words: [String: Word]
required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let words = try container.decode([Word].self)
self.words = [String: Word](uniqueKeysWithValues: words.map { (key: [=10=].id, value: [=10=]) })
for word in words {
word.parentDictionary = self
}
}
}
let decoder = JSONDecoder()
let dictionary = try decoder.decode(Dictionary.self, from: data)
print(dictionary.words["fr_chat"]?.translation(for: .english)?.name)