具有不同数组类型的Swift Codable

时间:2017-12-28 06:11:33

标签: swift codable

我正在编写一个程序来解析包含数组数组的JSON数据,其中嵌套数组具有不同的对象类型(具体来说,[[String,String,Int]] )。例如,

{
"number": 5295,
"bets": [
    [
        "16",
        "83",
        9
    ],
    [
        "75",
        "99",
        4
    ],
    [
        "46",
        "27",
        5
    ]
]
}

我试图使用可编码来帮助我解析数据,但当我尝试类似

struct OrderBook: Codable {
    let number: Int
    let bets: [Bet]
}

struct Bet: Codable {
    let price: String
    let sale: String
    let quantity: Int
}

它让我错误地说

  

预计会解码字典<String, Any>,但会找到一个数组

我如何解决这个问题?我无法声明一个空类型的数组。

3 个答案:

答案 0 :(得分:3)

正如您已经注意到[[Any]]不符合Decodable协议,因此您需要使用JSONSerialization jsonObject(with: Data)方法为您创建自定义初始值设定项结构:

struct OrderBook {
    let number: Int
    let bets: [Bet]
    init(data: Data) throws {
        let dict = try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:]
        number = dict["number"] as! Int
        bets = (dict["bets"] as! [[Any]]).map {
            Bet(price: $0[0] as! String, sale: $0[1] as! String, quantity:  $0[2] as! Int)
        }
    }
}
struct Bet {
    let price: String
    let sale: String
    let quantity: Int
}

测试:

let json = """
{"number": 5295,
 "bets": [
    ["16","83",9],
    ["75","99",4],
    ["46","27",5]
        ]
}
"""
let data = Data(json.utf8)
do {
    let orderBook = try OrderBook(data: data)
    print(orderBook)
} catch {
    print(error)
}

这将打印

  

订购簿(编号:5295,投注:[投注(价格:&#34; 16&#34;,销售:&#34; 83&#34;,数量:   9),投注(价格:&#34; 75&#34;,销售:&#34; 99&#34;,数量:4),投注(价格:&#34; 46&#34;,销售:   &#34; 27&#34;,数量:5)])

答案 1 :(得分:3)

一种解决方案(假设您无法更改JSON)是为Bet实现自定义解码逻辑。您可以使用未加密的容器(从JSON数组中读取)以依次解码每个属性(您调用decode(_:)的顺序是它们预期在数组中出现的顺序)。

import Foundation

struct OrderBook : Codable {
  let number: Int
  let bets: [Bet]
}

struct Bet : Codable {
  let price: String
  let sale: String
  let quantity: Int

  init(from decoder: Decoder) throws {
    var container = try decoder.unkeyedContainer()
    self.price = try container.decode(String.self)
    self.sale = try container.decode(String.self)
    self.quantity = try container.decode(Int.self)
  } 

  // if you need encoding (if not, make Bet Decodable
  // and remove this method)
  func encode(to encoder: Encoder) throws {
    var container = encoder.unkeyedContainer()
    try container.encode(price)
    try container.encode(sale)
    try container.encode(quantity)
  }
}

解码示例:

let jsonString = """
{ "number": 5295, "bets": [["16","83",9], ["75","99",4], ["46","27",5]] }
"""

let jsonData = Data(jsonString.utf8)

do {
  let decoded = try JSONDecoder().decode(OrderBook.self, from: jsonData)
  print(decoded)
} catch {
  print(error)
}

// OrderBook(number: 5295, bets: [
//   Bet(price: "16", sale: "83", quantity: 9),
//   Bet(price: "75", sale: "99", quantity: 4),
//   Bet(price: "46", sale: "27", quantity: 5)
// ])

答案 2 :(得分:-1)

这是糟糕的json结构,如果你可以从服务器上改变它,我建议:

SwiftyJSON

但是如果你不能改变那种json格式,我认为你应该使用Codable库来处理json。我讨厌{{1}}:)