-1

我刚刚学习了 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 的第一个实现并在最后一行稍作修改,我完成了我的工作。这是完整的工作代码:

业务.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"
    }
}

企业.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([])
    }
}
4

1 回答 1

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
}

我没有尝试任何这段代码,所以可能会有一些调整。此外,您可能不需要所有类型注释,但我添加它们是为了帮助解释代码。根据您的应用程序,您可能还可以在新结构模型之外使用此代码。

于 2017-05-04T16:34:27.277 回答