解码嵌套的JSON Swift

时间:2018-07-29 07:08:22

标签: json swift

我必须对响应下载的这种JSON类型进行解码。 JSON是这样,我需要检索所有项目的“图库” JSON:https://pastebin.com/KnEwZzxd 我尝试了许多解决方案,但无法创建此儿子的解码。 我也将完整的代码发布在pastebin上。

{
"status": 200,
"data": {
    "date": "2018-07-29T00:00:00.300Z",
    "featured": [
        {
            "id": "5b56298d781e197186378f50",
            "name": "Sun Tan Specialist",
            "price": "1,500",
            "priceIcon": "vbucks",
            "priceIconLink": "https://image.fnbr.co/price/icon_vbucks.png",
            "images": {
                "icon": "https://image.fnbr.co/outfit/5b56298d781e197186378f50/icon.png",
                "png": "https://image.fnbr.co/outfit/5b56298d781e197186378f50/png.png",
                "gallery": "https://image.fnbr.co/outfit/5b56298d781e197186378f50/gallery.jpg",
                "featured": "https://image.fnbr.co/outfit/5b56298d781e197186378f50/featured.png"
            },
            "rarity": "epic",
            "type": "outfit",
            "readableType": "Outfit"
        },
        {
            "id": "5b562af2781e19db65378f5c",
            "name": "Rescue Paddle",
            "price": "800",
            "priceIcon": "vbucks",
            "priceIconLink": "https://image.fnbr.co/price/icon_vbucks.png",
            "images": {
                "icon": "https://image.fnbr.co/pickaxe/5b562af2781e19db65378f5c/icon.png",
                "png": "https://image.fnbr.co/pickaxe/5b562af2781e19db65378f5c/png.png",
                "gallery": "https://image.fnbr.co/pickaxe/5b562af2781e19db65378f5c/gallery.jpg",
                "featured": false
            },
            "rarity": "rare",
            "type": "pickaxe",
            "readableType": "Pickaxe"
        }
    ],
    "daily": [
        {
            "id": "5ab1723e5f957f27504aa502",
            "name": "Rusty Rider",
            "price": "1,200",
            "priceIcon": "vbucks",
            "priceIconLink": "https://image.fnbr.co/price/icon_vbucks.png",
            "images": {
                "icon": "https://image.fnbr.co/glider/5ab1723e5f957f27504aa502/icon.png",
                "png": "https://image.fnbr.co/glider/5ab1723e5f957f27504aa502/png.png",
                "gallery": "https://image.fnbr.co/glider/5ab1723e5f957f27504aa502/gallery.jpg",
                "featured": "https://image.fnbr.co/glider/5ab1723e5f957f27504aa502/featured.png"
            },
            "rarity": "epic",
            "type": "glider",
            "readableType": "Glider"
        },
        {
            "id": "5b0e944bdb94f1a4bbc0a8e4",
            "name": "Rambunctious",
            "price": "500",
            "priceIcon": "vbucks",
            "priceIconLink": "https://image.fnbr.co/price/icon_vbucks.png",
            "images": {
                "icon": "https://image.fnbr.co/emote/5b0e944bdb94f1a4bbc0a8e4/icon.png",
                "png": "https://image.fnbr.co/emote/5b0e944bdb94f1a4bbc0a8e4/png.png",
                "gallery": "https://image.fnbr.co/emote/5b0e944bdb94f1a4bbc0a8e4/gallery.jpg",
                "featured": false
            }
    ]
}
}

2 个答案:

答案 0 :(得分:1)

除了发布JSON代码本身之外,实际显示尝试解码的方式也会很有用。^ _________ ^。

无论如何,解决此问题的最佳方法是使用自定义结构和Decodable Protocol处理JSON响应。

最初,您将从JSON中获得两个值:

/// The Initial Response From The Server
struct Response: Decodable {
    let status: Int
    let data: ResponseData

}

然后我们将“数据”映射到名为ResponseData的结构:

/// The Data Object
struct ResponseData: Decodable{
    let date: String
    let featured: [Product]
    let daily: [Product]
}

在此,我们有两个variables,其中包含一个我称为struct的相同Product数组:

/// The Product Structure
struct Product: Decodable{
    let id: String
    let name: String
    let price: String
    let priceIcon: String
    let priceIconLink: String
    let images: ProductImages
    let rarity: String
    let type: String
    let readableType: String
}

在此范围内,我们有一个variable,它是一个dictionary(图像),然后我们将其映射到另一个struct

/// The Data From The Product Images Dictionary
struct ProductImages: Decodable{
    let icon: String
    let png: String
    let gallery: String

    ///The Featured Variable For The Product Images Can Contain Either A String Or A Boolean Value
    let featured: FeaturedType

}

ProductImages的问题在于,精选的var有时包含一个String,但在其他情况下,它包含一个Bool的值。因此,我们需要创建一个自定义结构来处理此解码,以确保我们总是得到String(我可能做得不好,所以如果有人有更好的解决方案,请这样说):

/// Featured Type Returns A String Of Either The Boolean Value Or The Link To The JPG
struct FeaturedType : Codable {

    let formatted: String

    init(from decoder: Decoder) throws {

        let container =  try decoder.singleValueContainer()

        do {
            //1. If We Get A Standard Response We Have A String
            let stringResult = try container.decode(String.self)
            formatted = stringResult
        } catch {
            //2. On Occassions We Get An Bool
            let boolResult = try container.decode(Bool.self)
            formatted = String(boolResult)

        }
    }
}

现在这是JSON的基本结构,因此现在您需要处理它。在此示例中,由于我没有实际的JSON,因此正在从MainBundle加载URL

/// Loads & Decodes The JSON File
func retreiveJSON(){

    //1. Load The JSON File From The Main Bundle
    guard let jsonURL = Bundle.main.url(forResource: "sample", withExtension: ".json") else { return }

    do{
        //2. Get The Data From The URL
        let data = try Data(contentsOf: jsonURL)

        //3. Decode The JSON
        let jsonData = try JSONDecoder().decode(Response.self, from: data)

        //4. Extract The Data
        extractDataFrom(jsonData)

    }catch{
        print("Error Processing JSON == \(error)")
    }
}

在上面的函数中,您会注意到我正在调用函数extractDataFrom(),该函数可让您随后执行需要处理的数据:

/// Extracts The Daily & Featured Products From The JSON
///
/// - Parameter jsonData: Response
func extractDataFrom(_ jsonData: Response){

    //1. Get The Daily Products
    let dailyProducts = jsonData.data.daily

    dailyProducts.forEach { (product) in

        print(product.id)
        print(product.name)
        print(product.price)
        print(product.priceIcon)
        print(product.priceIconLink)
        print(product.images)
        print(product.rarity)
        print(product.type)
        print(product.readableType)
    }


    //2. Get The Featured Products
    let featuredProducts = jsonData.data.featured

    featuredProducts.forEach { (product) in

        print(product.id)
        print(product.name)
        print(product.price)
        print(product.priceIcon)
        print(product.priceIconLink)
        print(product.images)
        print(product.rarity)
        print(product.type)
        print(product.readableType)
    }
}

如果您想保存此数据,则只需在类声明下添加以下变量,例如:

var featuredProducts = [Product]()
var dailyProducts = [Product]()

然后在extractDataFrom()函数中更改:

 let dailyProducts
 let featuredProducts

收件人:

dailyProducts = jsonData.data.daily
featuredProducts = jsonData.data.featured

请注意,这是一个非常粗糙的示例,并且如上所述,我可能未正确处理“功能”变量。

希望有帮助...

答案 1 :(得分:-1)

感谢quicktype和其他将有效json转换为Swift和其他语言的服务非常简单。根据您的需求进行编辑应该足够简单。

// To parse the JSON, add this file to your project and do:
//
//   let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)

import Foundation

struct Welcome: Codable {
    let status: Int
    let data: DataClass
}

struct DataClass: Codable {
    let date: String
    let featured, daily: [Daily]
}

struct Daily: Codable {
    let id, name, price, priceIcon: String
    let priceIconLink: String
    let images: Images
    let rarity, type, readableType: String?
}

struct Images: Codable {
    let icon, png, gallery: String
    let featured: Featured
}

enum Featured: Codable {
    case bool(Bool)
    case string(String)

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

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