在 Swift 中使用 Codable 解析 JSON

Parsing JSON With Codable in Swift

我正在尝试用 Swift 中的 codable 解析 JSON。我以前已经成功地做到了这一点,但是我有一个更复杂的 JSON 对象和一些数组,我遇到了麻烦。

这是我的 JSON:

{
"data": [ {
    "type":"player",
    "id":"account.7e5b92e6612440349afcc06b7c390114",
    "attributes": {
        "createdAt":"2018-04-06T04:59:40Z",
        "name":"bob",
        "patchVersion":"",
        "shardId":"pc-na",
        "stats":null,
        "titleId":"bluehole-pubg",
        "updatedAt":"2018-04-06T04:59:40Z"
    },
    "relationships": {
        "assets": {
            "data":[]
        },
        "matches": {
            "data": [
            {"type":"match","id":"3e2a197a-1453-4569-b35b-99e337dfabc5"},
            {"type":"match","id":"15f41d2f-9da2-4b95-95ca-b85e297e14b7"},
            {"type":"match","id":"a42c496c-ad92-4d3e-af1f-8eaa2e200c2b"}
            {"type":"match","id":"b6e33df5-4754-49da-9a0f-144842bfc306"},
            {"type":"match","id":"5b357cd1-35fe-4859-a2d7-48f263120bbd"},
            {"type":"match","id":"99fc5f81-c24c-4c82-ae03-cd21c94469c0"},
            {"type":"match","id":"1851c88e-6fed-48e8-be84-769f20f5ee6f"},
            {"type":"match","id":"e16db7ea-520f-4db0-b45d-649264ac019c"},
            {"type":"match","id":"6e61a7e7-dcf5-4df5-aa88-89eca8d12507"},
            {"type":"match","id":"dcbf8863-9f7c-4fc9-b87d-93fe86babbc6"},
            {"type":"match","id":"0ba20fbb-1eaf-4186-bad5-5e8382558564"},
            {"type":"match","id":"8b104f3b-66d5-4d0a-9992-fe053ab4a6ca"},
            {"type":"match","id":"79822ea7-f204-47f8-ae6a-7efaac7e9c90"},
            {"type":"match","id":"1389913c-a742-434a-80c5-1373e115e3b6"}
            ]
        }
    },
    "links": {
        "schema":"",
        "self":"https://api.playbattlegrounds.com/shards/pc-na/players/account.7e5b92e6612440349afcc06b7c390114"
    }
}],
"links": {
    "self":"https://api.playbattlegrounds.com/shards/pc-na/players?filter[playerNames]=dchilds64"
    },
"meta":{}
}

以下是我使用的模型:

public struct PlayerResponse: Codable {
    let data: [Player]
}

玩家:

public struct Player: Codable {
    let type: String
    let id: String
    let attributes: Attributes
    let relationships: Relationships
}

对于属性:

public struct Attributes: Codable {
    let name: String
    let patchVersion: String
    let shardId: String
    let titleId: String
    let updatedAt: String
}

对于关系:

public struct Relationships: Codable {
    let matches: Matches
}

对于比赛:

public struct Matches: Codable {
    let data: [Match]
}

匹配:

public struct Match: Codable {
    let type: String
    let id: String

}

解码为:

let players = try decoder.decode([Player].self, from: jsonData)

我有这个功能运行我的网络请求:

    func getPlayerData(for name: String, completion: ((Result<[Player]>) -> Void)?) {
    var urlComponents = URLComponents()
    urlComponents.scheme = "https"
    urlComponents.host = "api.playbattlegrounds.com"
    urlComponents.path = "/shards/\(regionShard.rawValue)/players"
    let playerNameItem = URLQueryItem(name: "filter[playerNames]", value: "\(name)")
    urlComponents.queryItems = [playerNameItem]
    guard let url = urlComponents.url else { fatalError("Could not create URL from components") }
    print(url)

    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue("bearer \(apiKey)", forHTTPHeaderField: "Authorization")
    request.setValue("application/vnd.api+json", forHTTPHeaderField: "Accept")

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    let task = session.dataTask(with: request) { (responseData, response, responseError) in
        DispatchQueue.main.async {
            if let error = responseError {
                completion?(.failure(error))
            } else if let jsonData = responseData {
                let decoder = JSONDecoder()

                do {
                    let players = try decoder.decode([Player].self, from: jsonData)
                    completion?(.success(players))
                } catch {
                    completion?(.failure(error))
                }
            } else {
                let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
                completion?(.failure(error))
            }
        }
    }

    task.resume()
}

我面临的问题是当我尝试 运行 网络请求时出现此错误:

我认为我的可编码结构存在问题,但我不确定。有人可以指出正确的方向来查找我的错误吗?

据我所知,您的整个播放器响应都在关键 data 中。并且您直接使用 Player 可编码结构解析播放器信息,而不是 PlayerResponse 可编码结构中使用的 data 键。

要解决此问题,请将您的代码更新为:

let players = try decoder.decode(PlayerResponse.self, from: jsonData)

希望这能解决您的问题。

我建议您从头开始构建它,因为 JSONDecoder 的错误(与任何编译器一样)越复杂,您的结构越复杂。让我们看看我们能走多远:

您的 Match 结构非常合理:

public struct Match: Codable {
    let type: String
    let id: String

}

let decoder = JSONDecoder()
let mData = """
   {"type":"match","id":"3e2a197a-1453-4569-b35b-99e337dfabc5"}
   """.data(using:.utf8)!
let match = try! decoder.decode(Match.self, from:mData)

print(match)

这里没有意外的问题。缩短 Matches 一点你已经得到了第一个错误,而不是一个意外的错误

public struct Matches: Codable {
    let data: [Match]
}

let mtchsData = """
    {
        "data": [
            {"type":"match","id":"3e2a197a-1453-4569-b35b-99e337dfabc5"},
            {"type":"match","id":"15f41d2f-9da2-4b95-95ca-b85e297e14b7"},
            {"type":"match","id":"a42c496c-ad92-4d3e-af1f-8eaa2e200c2b"}
            {"type":"match","id":"b6e33df5-4754-49da-9a0f-144842bfc306"},
            {"type":"match","id":"5b357cd1-35fe-4859-a2d7-48f263120bbd"}
        ]
    }
    """.data(using:.utf8)!
do {
    let mtches = try decoder.decode(Matches.self, from:mtchsData)
    print(mtches)
} catch {
    print(error)
}

将打印以下错误:

"dataCorrupted(Swift.DecodingError.Context(codingPath: [],
debugDescription: "The given data was not valid JSON.", 
underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840
    "Badly formed array around character 233."
UserInfo={NSDebugDescription=Badly 
    formed array around character 233.})))\n"

这是一个小错误,您在 data Array 的第 3 行缺少一个逗号。添加一切都会顺利,但如果您的服务出现这种情况,您将必须先修复它。

我猜你明白了,并且知道如何连续构建结构。在顶层,您会注意到您的顶层结构超出了 Player 的数组,它实际上是一个 Dictionary,以 "data" 作为唯一键,因为您在 [=21] 中正确建模=] 正如@AnkitJayaswal 已经指出的那样。这已经造成了两个错误,那些是我设法轻松发现的错误,但正如我之前建议的那样,您应该继续构建测试,这样您就会知道 "lower" 级别正确解析并可以专注于手头的问题。

以上所有操作都可以在 Playground 中轻松完成,并且无需在此过程中实际调用 WebService。当然你必须 import Cocoa,但你已经知道了。无论如何,将您的问题分解成更小的部分总是有助于降低复杂性。