Swift 4.1 Decodable无法使用nestedContainer解码嵌套数组

时间:2018-04-23 04:48:29

标签: ios swift codable

尝试使用Codable编写一个简单的Swift 4.1来解析json

我有struct这样:

struct GameCharacter : Codable {
  var name : String
  var weapons : [Weapon]
  enum CodingKeys : String, CodingKey {
    case name
    case weapons
  }

  init(from decoder: Decoder) {
    do {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        let weaponsContainer = try container.nestedContainer(keyedBy: Weapon.CodingKeys.self, forKey: .weapons)
        self.weapons = try weaponsContainer.decode([Weapon].self, forKey: .weapons)

    } catch let error {
        print("error: \(error)")
        fatalError("error is \(error)")
    }
  }
}

以及其他类似内容:

struct Weapon : Codable {    
  var name : String
  enum CodingKeys : String, CodingKey {
    case name
  }

  init(from decoder: Decoder) {
    do {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
    } catch let error {
        print("error: \(error)")
        fatalError("error is \(error)")
    }
  }
}

我还有一个struct这样的包装器:

struct Game : Codable {
  var characters : [GameCharacter]
  enum CodingKeys : String, CodingKey { case characters }
}

json数据如下所示:

{ 
  "characters" : [{
    "name" : "Steve",
    "weapons" : [{
      "name" : "toothpick"
    }]
  }]
}

但是,我总是收到typeMismatcherror错误:

  

错误:typeMismatch(Swift.Dictionary,   Swift.DecodingError.Context(codingPath:[CodingKeys(stringValue:   “characters”,intValue:nil),_ JSONKey(stringValue:“Index 0”,   intValue:0)],debugDescription:“预计要解码   字典却找到了一个数组。“,underlyingError:   无))

在这一行:

let weaponsContainer = try container.nestedContainer(keyedBy: Weapon.CodingKeys.self, forKey: .weapons)

我不确定问题是什么,因为我(在我看来)要求提供一系列武器,但它认为我正在寻找一本字典。

想知道是否有人对我遗失的内容有任何见解。

4 个答案:

答案 0 :(得分:3)

只有在想要将子字典或子数组解码为父结构时才需要

nestedContainers - 例如将weapons对象解码为Game结构 - 这是不是这种情况,因为你声明了所有嵌套的结构。

要解码JSON,您可以省略所有CodingKeys和初始值设定项,利用Codable的魔力,这就足够了:

struct Game : Codable {
    let characters : [GameCharacter]
}

struct GameCharacter : Codable {
    let name : String
    let weapons : [Weapon]
}

struct Weapon : Codable {
    let name : String
}

并将其命名为

do {
    let result = try JSONDecoder().decode(Game.self, from: data)
    print(result)
} catch { print(error) }

答案 1 :(得分:0)

替换您的结构,不需要任何自定义初始值设定项

import Foundation

struct Weapon: Codable {
    let characters: [Character]
}

struct Character: Codable {
    let name: String
    let weapons: [WeaponElement]
}

struct WeaponElement: Codable {
    let name: String
}

并创建

extension Weapon {
init(data: Data) throws {
    self = try JSONDecoder().decode(Weapon.self, from: data)
}

现在只是

 let weapon = try Weapon(json)

答案 2 :(得分:0)

试试这个

let string = """
{
"characters" : [{
    "name" : "Steve",
    "weapons" : [{
    "name" : "toothpick"
        }]
    }]
}
"""

struct GameCharacter: Codable {
    let characters: [Character]
}

struct Character: Codable {
    let name: String
    let weapons: [Weapon]
}

struct Weapon: Codable {
    let name: String
}

let jsonData = string.data(using: .utf8)!
let decodr = JSONDecoder()

let result = try! decodr.decode(GameCharacter.self, from: jsonData)

let weapon = result.characters.flatMap {$0.weapons}

for weaponname in weapon {
    print(weaponname.name) //Output toothpick
}

答案 3 :(得分:0)

我有同样的问题,JSONDecoder()仅解码JSON的第一级,然后我用从Codable扩展的类的主体中注释了这些方法来解决此问题。

public class Response<T:Codable> : Codable {

    public let data : T?

//commented this two function and my problem Solved <3
//    enum CodingKeys: String, CodingKey {
//        case data
//    }
//    required public init(from decoder: Decoder) throws {
//        data = try T(from: decoder)
//    }


}