【问题标题】:Unwrapping JSON arrays into Swift objects将 JSON 数组解包成 Swift 对象
【发布时间】:2021-06-11 16:43:53
【问题描述】:

我是 Swift 新手,正在尝试将 HTTP 响应解包到 Swift 对象中。以下是我得到的响应格式。

{
    "data": {
        "task": [
            {
                "id": "65eea256-e497-4426-b462-18790bdaf9da",
                "title": "First task",
                "user_id": "zR3vTSf771crIau65gkrZnVzRIj2",
                "updated_at": "2021-06-09T18:38:45.300793+00:00",
                "created_at": "2021-06-09T18:38:45.300793+00:00"
            },
        ]
    }
}

我想将上述"task" 的JSON 数组转换成[Task] 的Swift 数组。

问题:"task" 部分是唯一在 API 调用结果中有所不同的部分。换句话说,如果我要查询 projects,它会在 JSON 中显示"project"(代替“task”),同时保持整体结构不变。

这是我使结果通用的尝试,但我缺少 JSON 属性 "task"。如果我在结构中添加属性,那么它将无法跨查询重用,并且我最终会得到每个类型的结果结构。

struct Result<T:Codable> : Codable {
    let data: [T]
}

这映射到(这是错误的!)

{
    "data": [
        {
            "id": "65eea256-e497-4426-b462-18790bdaf9da",
            "title": "First task",
            "user_id": "zR3vTSf771crIau65gkrZnVzRIj2",
            "updated_at": "2021-06-09T18:38:45.300793+00:00",
            "created_at": "2021-06-09T18:38:45.300793+00:00"
        },
    ]
}

【问题讨论】:

  • 请不要将您的对象称为Result。这将与Swift.Result 发生冲突。
  • @Sulthan 感谢您指出这一点。对我来说绝对是新事物:)

标签: ios swift codable


【解决方案1】:

如果你手动编写解码过程,你可以做任何你想做的事情:

struct Response<T: Decodable>: Decodable {
    let data: [T]

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: RawCodingKey.self)
        guard let firstKey = container.allKeys.first else {
            throw ResponseError.couldNotFindData
        }
        data = try container.decode([T].self, forKey: firstKey)
    }

    private enum ResponseError: String, Error {
        case couldNotFindData
    }

    private struct RawCodingKey: CodingKey {
        let stringValue: String
        let intValue: Int? = nil

        init?(stringValue: String) {
            self.stringValue = stringValue
        }

        init?(intValue: Int) {
            return nil
        }
    }
 }

我只写了解码过程,因为编码会是一个更大的问题。

还要注意data 中的任何其他键都会破坏它。当然,您也可以使用allKeys.filter { ... } 过滤掉其他数据。

【讨论】:

  • 您好,谢谢您的回答!我假设else 部分解码为我想要的编码密钥,即我可以说.project 它应该可以正常工作。我无法理解 if 声明以及 singleValueContainer() 到底是什么。
  • @SiddharthKamaria 我错过了您问题中关于project 的部分。总是只有一把钥匙吗?然后,您可能可以通过枚举键并获取第一个键来使其更具动态性。请参阅container 上的方法。单值容器基本上是您想要整体解码的任何内容。它只需要传递给此解码器的所有内容。
  • 所以它总是以"data"开头,然后它可以是"task""project"subtask"或任何东西。然后将 JSON 数组映射到相关类型T。我会通过container()singleValueContainer() 来更好地理解它!
  • @SiddharthKamaria 更新了,它不会只取第一个密钥并从中解码[T]
  • 我认为这正是我所需要的,但我一定会尝试并告诉你。关于“请注意,任何其他关键的内部数据都会破坏它。” -- 我在 JSON 中只得到“数据”或“错误”,但不是两者都有,所以我认为这应该没问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-13
  • 1970-01-01
  • 2015-08-18
  • 1970-01-01
  • 2020-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多