如何解码不同类型的 JSON 属性? [英] How to decode a JSON property with different types?

查看:22
本文介绍了如何解码不同类型的 JSON 属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 JSON

{
"tvShow": {
    "id": 5348,
    "name": "Supernatural",
    "permalink": "supernatural",
    "url": "http://www.episodate.com/tv-show/supernatural",
    "description": "Supernatural is an American fantasy horror television series created by Eric Kripke. It was first broadcast on September 13, 2005, on The WB and subsequently became part of successor The CW's lineup. Starring Jared Padalecki as Sam Winchester and Jensen Ackles as Dean Winchester, the series follows the two brothers as they hunt demons, ghosts, monsters, and other supernatural beings in the world. The series is produced by Warner Bros. Television, in association with Wonderland Sound and Vision. Along with Kripke, executive producers have been McG, Robert Singer, Phil Sgriccia, Sera Gamble, Jeremy Carver, John Shiban, Ben Edlund and Adam Glass. Former executive producer and director Kim Manners died of lung cancer during production of the fourth season.<br>The series is filmed in Vancouver, British Columbia and surrounding areas and was in development for nearly ten years, as creator Kripke spent several years unsuccessfully pitching it. The pilot was viewed by an estimated 5.69 million viewers, and the ratings of the first four episodes prompted The WB to pick up the series for a full season. Originally, Kripke planned the series for three seasons but later expanded it to five. The fifth season concluded the series' main storyline, and Kripke departed the series as showrunner. The series has continued on for several more seasons with Sera Gamble and Jeremy Carver assuming the role of showrunner.",
    "description_source": "http://en.wikipedia.org/wiki/Supernatural_(U.S._TV_series)#Spin-off_series",
    "start_date": "2005-09-13",
    "end_date": null,
    "country": "US",
    "status": "Running",
    "runtime": 60,
    "network": "The CW",
    "youtube_link": "6ZlnmAWL59I",
    "image_path": "https://static.episodate.com/images/tv-show/full/5348.jpg",
    "image_thumbnail_path": "https://static.episodate.com/images/tv-show/thumbnail/5348.jpg",
    "rating": "9.6747",
    "rating_count": "249",
    "countdown": null
}
}

rating在不同序列中的值为Int(rating":0)或String(rating":9.6747").

The value of rating in different serials is Int ("rating": 0) or String ("rating": "9.6747").

我正在使用 Codable/Decodable 协议解析 JSON:

I am parsing JSON with Codable/Decodable protocols:

struct DetailModel : Decodable {

var id : Int?
var name : String?
var permalink : String?
var url : String?
var description : String
var description_source : String?
var start_date : String?
var end_date : String?
var country : String?
var status : String?
var runtime : Int?
var network : String?
var youtube_link : String?
var image_path : String?
var image_thumbnail_path : String?
var rating: String
var rating_count : String?
var countdown : String?
var genres : [String]?
var pictures : [String]?
var episodes : [EpisodesModel]?
}

如果 rating == String,我的代码可以工作并且我拥有来自 JSON 的所有变量,但是如果 rating == Int,一切都是 nil.我应该怎么做才能一次解析所有类型的变量 rating IntString?

If rating == String, my code work and I have all the variables from JSON, but if rating == Int, all is nil. What should I do to parse all the types of variable rating at once Int and String?

我的可解码函数:

    func searchSerialDetail(id: Int, completion: @escaping (DetailModel?) -> Void){

    let parameters: [String: Any] = ["q": id]

    Alamofire.request(DetailNetworkLayer.url, method: .get, parameters: parameters).response { (jsonResponse) in

        if let jsonValue =  jsonResponse.data {
            let jsonDecoder = JSONDecoder()
                let detail = try? jsonDecoder.decode(DetailModel.self, from: jsonValue)
                completion(detail)
        }
    }
}

谢谢.

推荐答案

你将不得不实现你自己的 func encode(to encoder: Encoder) throwsinit(fromdecoder: Decoder) throwsCodable 协议的两个属性.然后将您的 rating 变量更改为 enum

You will have to implement your own func encode(to encoder: Encoder) throws and init(from decoder: Decoder) throws which are both properties of the Codable protocol. Then change your rating variable into an enum

看起来像这样:

enum Rating: Codable {
    case int(Int)
    case string(String)

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

    init(from decoder: Decoder) throws {
        let value = try decoder.singleValueContainer()

        if let v = try? value.decode(Int.self) {
            self = .int(v)
            return
        } else if let v = try? value.decode(String.self) {
            self = .string(v)
            return
        }

        throw Rating.ParseError.notRecognizedType(value)
    }

    enum ParseError: Error {
        case notRecognizedType(Any)
    }
}

然后在您的 DetailModel 上只需将 rating: String 更改为 rating: Rating

Then on your DetailModel just change rating: String to rating: Rating

这有效,我已经用这些 JSON 字符串进行了测试.

This works, I have tested with these JSON strings.

// int rating
{   
    "rating": 200,
    "bro": "Success"
}

// string rating
{
    "rating": "200",
    "bro": "Success"
}

编辑:我找到了一种更好更快捷的方式来实现 init(fromdecoder: Decoder) throws,它会产生更好的错误消息,通过使用它,您可以现在省略 ParseError 枚举.

Edit: I've found a better swiftier way of implementing init(from decoder: Decoder) throws, which produces a better error message, by using this you can now omit the ParseError enum.

init(from decoder: Decoder) throws {
    let value = try decoder.singleValueContainer()
    do {
        self = .int(try value.decode(Int.self))
    } catch DecodingError.typeMismatch {
        self = .string(try value.decode(String.self))
    }
}

这篇关于如何解码不同类型的 JSON 属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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