簡體   English   中英

使用 codable 解碼 JSON 數組 - Swift 5

[英]Decoding JSON array with codable - Swift 5

我想解碼以下鏈接中的json數據

https://stats.nba.com/stats/assistleaders?LeagueID=00&PerMode=Totals&PlayerOrTeam=Team&Season=2019-20&SeasonType=Regular+Season

但是,rowSet 數據是數組內部的一組 json 數組。 如何設置可編碼結構來解碼此數據? 我能夠設置以下結構來解碼其他數據。

import Foundation

struct leagueLeader: Codable {
    
    var resource: String
    var parameters: parameters
    
}

struct parameters: Codable {
    var LeagueID: String
    var PerMode: String
    var StatCategory: String
    var Season: String
    var SeasonType: String
}

struct resultSet: Codable {
    
    var name: String
    var headers: [String]

}

困難的部分是處理應該包含多種數據類型的rowSet

{
  "rowSet": [
    [
      1,
      1610612756,
      "PHX",
      "Phoenix Suns",
      1987
    ]
  ]
}

解決方案是聲明一個枚舉,每個 case 都有一個表示可編碼數據類型的關聯值

enum RowSet: Codable, Equatable {
    case integer(Int)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Int.self) {
            self = .integer(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        throw DecodingError.typeMismatch(RowSet.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for RowSet"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .integer(let x):
            try container.encode(x)
        case .string(let x):
            try container.encode(x)
        }
    }
}

型號齊全:

import Foundation

struct Leader: Codable, Equatable {
    let resource: String?
    let parameters: Parameters?
    let resultSets: [ResultSet]?
}

struct Parameters: Codable, Equatable {
    let leagueId, season, seasonType, perMode: String?
    let playerOrTeam: String?

    enum CodingKeys: String, CodingKey {
        case leagueId = "LeagueID"
        case season = "Season"
        case seasonType = "SeasonType"
        case perMode = "PerMode"
        case playerOrTeam = "PlayerOrTeam"
    }
}

struct ResultSet: Codable, Equatable {
    let name: String?
    let headers: [String]?
    let rowSet: [[RowSet]]?
}

enum RowSet: Codable, Equatable {
    case integer(Int)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Int.self) {
            self = .integer(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        throw DecodingError.typeMismatch(RowSet.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for RowSet"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .integer(let x):
            try container.encode(x)
        case .string(let x):
            try container.encode(x)
        }
    }
}

這種 JSON 結構非常不尋常。 它是一種逗號分隔值 (CSV),無法隱式解碼為結構體。

一個解決方案是手動解碼數組

struct RowSet: Decodable {
    let rank, teamID : Int
    let teamAbbreviation, teamName : String
    let ast : Int
    
    init(from decoder: Decoder) throws {
        var arrayContainer = try decoder.unkeyedContainer()
        guard arrayContainer.count == 5 else { throw DecodingError.dataCorruptedError(in: arrayContainer, debugDescription: "The array must contain 5 items") }
        rank = try arrayContainer.decode(Int.self)
        teamID = try arrayContainer.decode(Int.self)
        teamAbbreviation = try arrayContainer.decode(String.self)
        teamName = try arrayContainer.decode(String.self)
        ast = try arrayContainer.decode(Int.self)
    }
    
}

正如評論中提到的,請遵守命名約定:結構、類和枚舉以大寫字母開頭,變量、屬性、函數和枚舉案例以小寫字母開頭。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM