Swift 4 Codable解码json

7

我尝试实现新的Codable协议,所以我将Codable添加到我的结构体中,但是我在解码JSON时卡住了

以下是之前的代码:

结构体 -

struct Question {
    var title: String
    var answer: Int
    var question: Int
}

Client -

...

guard let data = data else {
    return
}

do {
    self.jsonResponse = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any]
    let questionItems = self.jsonResponse?["themes"] as! [[String: Any]]

    questionItems.forEach {
        let item = Question(title: $0["title"] as! String,
                            answer: $0["answer"] as! Int,
                            question: $0["question"] as! Int)
        questionData.append(item)
    }

} catch {
    print("error")
}

这是我现在拥有的内容,但我无法弄清解码器部分:
结构体 -
struct Question: Codable {
    var title: String
    var answer: Int
    var question: Int
}

Client -

...

let decoder = JSONDecoder()
if let questions = try? decoder.decode([Question].self, from: data) {
    // Can't get past this part
} else {
    print("Not working")
}

因为我无法通过decoder.decode部分,所以它会打印“不起作用”。有什么想法吗?如有需要,将发布任何额外的代码,谢谢!

编辑:

API JSON示例:

{
  "themes": [
    {
      "answer": 1,
      "question": 44438222,
      "title": "How many letters are in the alphabet?"
    },
    {
      "answer": 0,
      "question": 44438489,
      "title": "This is a random question"
    }
  ]
 }

如果我打印 self.jsonResponse,我会得到以下结果:
Optional(["themes": <__NSArrayI 0x6180002478f0>(
{
    "answer" = 7;
    "question" = 7674790;
    title = "This is the title of the question";
},
{
    "answer_" = 2;
    "question" = 23915741;
    title = "This is the title of the question";
}

我的新代码:

struct Theme: Codable {
    var themes : [Question]
}

struct Question: Codable {
    var title: String
    var answer: Int
    var question: Int
}

...

if let decoded = try? JSONDecoder().decode(Theme.self, from: data) {
    print("decoded:", decoded)
} else {
    print("Not working")
}

打印出错误并查看其内容。 - Kevin
2
这个程序相关的内容翻译成中文:在你的第一个例子中,它不能处理解码为jsonResponse的数据,因为那是一个JSON对象,而不是一个数组。你需要从"themes"键中获取数组(定义另一个具有此属性的Codable类型)。 - Hamish
@Hamish 是的,这正是我所想的,但我还不完全确定,谢谢! - SRMR
3个回答

8
如果您的JSON具有结构:
{"themes" : [{"title": "Foo", "answer": 1, "question": 2},
             {"title": "Bar", "answer": 3, "question": 4}]}

你需要一个等效的themes对象。请添加以下结构体:
struct Theme : Codable {
    var themes : [Question]
}

现在,您可以解析JSON:
if let decoded = try? JSONDecoder().decode(Theme.self, from: data) {
    print("decoded:", decoded)
} else {
    print("Not working")
}

包含Question对象的解码是隐式的。


谢谢!我觉得这正是我在对整体想法的具体性方面所寻找的内容。if let decoded 应该放在我的 do 语句内还是外部? - SRMR
如果你想使用 do-catch,你不需要 if,但是 decode 行必须在 do 范围内。 - vadian
完美。我已经将我的JSON添加到原始问题中,看起来像你想的那样吗?当我运行新代码时,仍然出现“未工作”的情况,尽管我知道我离正确答案很近,但我无法弄清楚问题所在。对我来说很难调试if let decoded语句以找出问题所在。 - SRMR
你是否添加了 Theme 结构体并将 decode(Question.self 更改为 decode(Theme.self - vadian
1
是的,属性的名称(和编号)必须与JSON中的键完全匹配。如果您想要不同的属性名称,则必须添加一个枚举CodingKeys:String,CodingKey {}并将案例(JSON键)映射到其原始值(属性名称)。为了调试,您不能使用try?将该行包装在do-catch块中,并在catch子句中打印错误。 - vadian
显示剩余5条评论

1
您看到此错误是因为您的JSON可能结构如下:
{
  "themes": [
    { "title": ..., "question": ..., "answer": ... },
    { "title": ..., "question": ..., "answer": ... },
    { ... }
  ],
  ...
}

然而,您编写的代码期望顶层是一个 [Question]。您需要一个不同的顶层类型,它具有一个名为 themes 的属性,该属性是一个 [Question]。当您解码该顶层类型时,您的 [Question] 将被解码到 themes 键中。

有道理,我会开始实现,谢谢! - SRMR

0

大家好,我已经添加了Swift 4的JSON编码和解码代码。

请使用链接这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接