如何使用Argo解码[String:Any]

时间:2017-05-04 11:20:19

标签: swift functional-programming argo

我刚刚学习了Argo基础知识,并且能够解码99%的JSON。现在我面临以下结构(像“5447”和“5954”这样的键是动态的)并需要帮助:

{
  "5447": {
    "business_id": 5447,
    "rating": 5,
    "comment": "abcd",
    "replies_count": 0,
    "message_id": 2517
  },
  "5954": {
    "business_id": 5954,
    "rating": 3,
    "comment": "efgh",
    "replies_count": 0,
    "message_id": 633
  }
}

Argo解码的典型示例如下:

struct User {
  let id: Int
  let name: String
}

用于JSON结构(键是固定的“id”和“name”):

{
  "id": 124,
  "name": "someone"
}

使用类似的东西:

extension User: Decodable {
  static func decode(j: JSON) -> Decoded<User> {
    return curry(User.init)
      <^> j <| "id"
      <*> j <| "name"
  }
}

但是我需要解析的数据结构不适合这个例子。

更新:使用Tony的第一个实现,在最后一行进行了一些小修改,我完成了我的工作。这是完整的工作代码:

Business.swift

import Argo
import Curry
import Runes

struct Business {
    let businessID: Int
    let rating: Double?
    let comment: String?
    let repliesCount: Int?
    let messageID: Int?
}

extension Business: Decodable {
    static func decode(_ json: JSON) -> Decoded<Business> {
        let c0 = curry(Business.init)
            <^> json <| "business_id"
            <*> json <|? "rating"
        return c0
            <*> json <|? "comment"
            <*> json <|? "replies_count"
            <*> json <|? "message_id"
    }
}

Businesses.swift

import Argo
import Runes

struct Businesses {
    let businesses: [Business]
}

extension Businesses: Decodable {
    static func decode(_ json: JSON) -> Decoded<Businesses> {
        let dict = [String: JSON].decode(json)
        let arr = dict.map { Array($0.map { $1 }) }
        let jsonArr = arr.map { JSON.array($0) }
        return Businesses.init <^> jsonArr.map([Business].decode).value ?? .success([])
    }
}

1 个答案:

答案 0 :(得分:4)

如果字典中的键是动态的,那么您必须走出Argo提供的便捷操作符。您首先需要获取JSON对象元素,然后自己映射它。由于密钥在这里是无关紧要的(因为id也在嵌入式字典中),它实际上不会太糟糕。最简单的方法是创建一个新结构来包装它:

struct Businesses: Decodable {
  let businesses: [Business]

  static func decode(_ json: JSON) -> Decoded<Businesses> {
    // Get dictionary
    let dict: Decoded<[String: JSON]> = [String: JSON].decode(json)
    // Transform dictionary to array
    let array: Decoded<[JSON]> = dict.map { Array($0.map { $1 }) }
    // Wrap array back into a JSON type
    let jsonArray: Decoded<JSON> = array.map { JSON.array($0) }
    // Decode the JSON type like we would with no key
    return Businesses.init <^> array.map([Business].decode)
  }
}

我们在这里做的是获取字典并将其转换为数组,以便我们可以像任何其他数组一样对其进行解码。

你也可以跳过转换到数组部分并从字典中解码它,如下所示:

static func decode(_ json: JSON) -> Decoded<Businesses> {
  // Get dictionary
  let dict: Decoded<[String: JSON]> = [String: JSON].decode(json)
  // Map over the dictionary and decode the values
  let result: Decoded<[Businesses]> = dict.flatMap { object in
    let decoded: [Decoded<Business>] = Array(object.map { decode($1) })
    return sequence(decoded)
  }
  return Businesses.init <^> result
}

我没有尝试任何此类代码,因此可能会有一些调整。此外,您可能不需要所有类型注释,但我添加它们以帮助解释代码。您也可以在新结构模型之外使用此代码,具体取决于您的应用程序。