使用可选值解码嵌套JSON Swift 4 [英] Decoding Nested JSON with Optional Values Swift 4

查看:81
本文介绍了使用可选值解码嵌套JSON Swift 4的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从此URL https://jsonodds.com/api/test解码此JSON / odds

I am trying to decode this JSON from this url https://jsonodds.com/api/test/odds

有时某些值是nil,我想避免将所有属性都设为可选,所以我尝试实现自定义解码。

Sometimes some of the values are nil and I wanted to avoid making all of my properties optional so I am trying to implement custom decoding.

[
    {
        "ID": "e83bf033-dbde-49ac-a073-d385ebdc66d1",
        "HomeTeam": "Xavier",
        "AwayTeam": "Marquette",
        "Sport": 2,
        "MatchTime": "2018-01-24T23:30:00",
        "Odds": [
            {
                "ID": "50742eed-a1c8-4958-8678-50627ffb665e",
                "EventID": "e83bf033-dbde-49ac-a073-d385ebdc66d1",
                "MoneyLineHome": "0",
                "MoneyLineAway": "0",
                "PointSpreadHome": "-4.0",
                "PointSpreadAway": "4.0",
                "PointSpreadHomeLine": "-110",
                "PointSpreadAwayLine": "-110",
                "TotalNumber": "78.0",
                "OverLine": "-115",
                "UnderLine": "-105",
                "DrawLine": "0",
                "LastUpdated": "2018-01-24T20:50:17",
                "SiteID": 3,
                "OddType": "First Half"
            },
            {
                "ID": "e83bf033-dbde-49ac-a073-d385ebdc66d1",
                "EventID": "e83bf033-dbde-49ac-a073-d385ebdc66d1",
                "MoneyLineHome": "-305",
                "MoneyLineAway": "255",
                "PointSpreadHome": "-7.0",
                "PointSpreadAway": "7.0",
                "PointSpreadHomeLine": "-112",
                "PointSpreadAwayLine": "-108",
                "TotalNumber": "162.5",
                "OverLine": "-104",
                "UnderLine": "-116",
                "DrawLine": "0",
                "LastUpdated": "2018-01-24T20:50:17",
                "SiteID": 3,
                "OddType": "Game"
            }
        ],
        "Details": "Marquette at Xavier",
        "HomeROT": "720",
        "AwayROT": "719"
    },

我目前有这个,但我不会知道我是否在考虑这些值何时为零,并且也很难使它起作用,因为JSON中的赔率键是一个数组,所以我不确定该怎么做。当JSON中的 OddType等于 Game时,我只需要 Odds。在我从JSON获取所有数据之后,我只是要过滤掉该数据,因为我不确定在解析JSON时是否有办法做到这一点。

I currently have this but I don't know if I am accounting for when the values are nil, and also am struggling to make it work since the "Odds" key in the JSON is an array, so I was not sure how to go about doing that. I only need the "Odds" for when the "OddType" in the JSON is equal to "Game". I was just gonna filter that out after I got all of the data from the JSON, since I wasn't sure if there was a way to do this while parsing the JSON.

struct GameInformation: Codable {    
    let homeTeam: String
    let awayTeam: String
    let sport: Int
    let matchTime: String
    let odds: [Odds]
    let details: String
}

struct Odds: Codable {
    let moneyLineHome: String
    let moneyLineAway: String
    let pointSpreadHome: String
    let pointSpreadAway: String
    let pointSpreadHomeLine: String
    let pointSpreadAwayLine: String
    let totalNumber: String
    let overLine: String
    let underLine: String
    let drawLine: String
    let lastUpdated: String
    let oddType: String
}

extension Odds {
    enum CodingKeys: String, CodingKey {
        case moneyLineHome = "MoneyLineHome"
        case moneyLineAway = "MoneyLineAway"
        case pointSpreadHome = "PointSpreadHome"
        case pointSpreadAway = "PointSpreadAway"
        case pointSpreadHomeLine = "PointSpreadHomeLine"
        case pointSpreadAwayLine = "PointSpreadAwayLine"
        case totalNumber = "TotalNumber"
        case overLine = "OverLine"
        case underLine = "UnderLine"
        case drawLine = "DrawLine"
        case lastUpdated = "LastUpdated"
        case oddType = "OddType"
    }

    enum OddsKey: String, CodingKey {
        case odds = "Odds"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: OddsKey.self)
        let oddsContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .odds)

        moneyLineHome = try oddsContainer.decodeIfPresent(String.self, forKey: .moneyLineHome) ?? "N/A"
        moneyLineAway = try oddsContainer.decodeIfPresent(String.self, forKey: .moneyLineAway) ?? "N/A"
        pointSpreadHome = try oddsContainer.decodeIfPresent(String.self, forKey: .pointSpreadHome) ?? "N/A"
        pointSpreadAway = try oddsContainer.decodeIfPresent(String.self, forKey: .pointSpreadAway) ?? "N/A"
        pointSpreadHomeLine = try oddsContainer.decodeIfPresent(String.self, forKey: .pointSpreadHomeLine) ?? "N/A"
        pointSpreadAwayLine = try oddsContainer.decodeIfPresent(String.self, forKey: .pointSpreadAwayLine) ?? "N/A"
        totalNumber = try oddsContainer.decodeIfPresent(String.self, forKey: .totalNumber) ?? "N/A"
        overLine = try oddsContainer.decodeIfPresent(String.self, forKey: .overLine) ?? "N/A"
        underLine = try oddsContainer.decodeIfPresent(String.self, forKey: .underLine) ?? "N/A"
        drawLine = try oddsContainer.decodeIfPresent(String.self, forKey: .drawLine) ?? "N/A"
        lastUpdated = try oddsContainer.decodeIfPresent(String.self, forKey: .lastUpdated) ?? "N/A"
        oddType = try oddsContainer.decodeIfPresent(String.self, forKey: .oddType) ?? "N/A"
    }
}

extension GameInformation {
    enum CodingKeys: String, CodingKey {
        case homeTeam = "HomeTeam"
        case awayTeam = "AwayTeam"
        case sport = "Sport"
        case matchTime = "MatchTime"
        case odds = "Odds"
        case details = "Details"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        homeTeam = try container.decodeIfPresent(String.self, forKey: .homeTeam) ?? "N/A"
        awayTeam = try container.decodeIfPresent(String.self, forKey: .awayTeam) ?? "N/A"
        sport = try container.decodeIfPresent(Int.self, forKey: .sport) ?? 20
        matchTime = try container.decodeIfPresent(String.self, forKey: .matchTime) ?? "N/A"
        details = try container.decodeIfPresent(String.self, forKey: .details) ?? "N/A"
        odds = [try Odds(from: decoder)]
    }
}

任何帮助深表感谢!

推荐答案

尝试在GameInformation初始化中设置如下赔率:

Try setting odds like this in the GameInformation init:

odds = try container.decode(Array<Odds>.self, forKey: .odds)

下一步,您应该删除OddsKey枚举并更新Odds init:

Next you should remove OddsKey enum and update Odds init:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    moneyLineHome = try container.decodeIfPresent(String.self, forKey: .moneyLineHome) ?? "N/A"
    moneyLineAway = try container.decodeIfPresent(String.self, forKey: .moneyLineAway) ?? "N/A"
    pointSpreadHome = try container.decodeIfPresent(String.self, forKey: .pointSpreadHome) ?? "N/A"
    pointSpreadAway = try container.decodeIfPresent(String.self, forKey: .pointSpreadAway) ?? "N/A"
    pointSpreadHomeLine = try container.decodeIfPresent(String.self, forKey: .pointSpreadHomeLine) ?? "N/A"
    pointSpreadAwayLine = try container.decodeIfPresent(String.self, forKey: .pointSpreadAwayLine) ?? "N/A"
    totalNumber = try container.decodeIfPresent(String.self, forKey: .totalNumber) ?? "N/A"
    overLine = try container.decodeIfPresent(String.self, forKey: .overLine) ?? "N/A"
    underLine = try container.decodeIfPresent(String.self, forKey: .underLine) ?? "N/A"
    drawLine = try container.decodeIfPresent(String.self, forKey: .drawLine) ?? "N/A"
    lastUpdated = try container.decodeIfPresent(String.self, forKey: .lastUpdated) ?? "N/A"
    oddType = try container.decodeIfPresent(String.self, forKey: .oddType) ?? "N/A"
}

我没有测试过,但是应该可以。关键是,我们应该将赔率解析为init内的单个对象,并将其解析为GameInformation init内的一个集合。我希望这是有道理的。

I haven't tested it, but it should work. The thing is, we should parse Odds as a single object inside it's init, and as a collection inside of GameInformation init. I hope it makes sense.

我认为最好在解析后进行过滤,您可以在GameInformation init中初始化这样的赔率(但是如果没有,这似乎是不正确的

I think it's better to filter after parsing, you can initialize odds like this in the GameInformation init (but it just doesn't seem right without utilizing optionals):

odds = try container.decode(Array<Odds>.self, forKey: .odds).filter {
        let mirror = Mirror(reflecting: $0)
        return !mirror.children.contains(where: { _, value in
            if let v = value as? String, v == "N/A" {
                return true
            } else {
                return false
            }
        })
    }

这篇关于使用可选值解码嵌套JSON Swift 4的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆