-2

I have to decode this type of JSON that is downloaded by a response. The JSON is this, I need retrieve the "gallery" of all items JSON: https://pastebin.com/KnEwZzxd I have tried many solution but I am not able to create a Decode of this son. I have posted the full code on pastebin, too.

{
"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
            }
    ]
}
}
  • Please add a sample of the JSON, and some code you've tried. – Cristik Jul 29 '18 at 07:49
  • Where is the rest or the question?! you should add the Json as well as the code that you have tried so far. – Ahmad F Jul 29 '18 at 08:09
  • I have edited the post –  Jul 29 '18 at 08:28
  • Please read https://stackoverflow.com/questions/39423367/correctly-parsing-json-in-swift-3/39423764#39423764. There’s quick tutorial how to distinguish the JSON types, – vadian Jul 29 '18 at 08:38

2 Answers2

1

Aside from posting the JSON Code itself, it would be useful to actually show an attempt as to how you have attempted to decode it as well ^_________^.

Anyway, the best way to tackle this issue is to use custom Structs and the Decodable Protocol to handle the JSON response.

From your JSON you will initially get a two values:

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

}

From this we then map the 'data' to a struct called ResponseData:

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

In this we have two variables which contain an array of identical struct which I have called 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
}

Within this we have one variable which is a dictionary (images) which we then map to another 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

}

The issue you have with the ProductImages, is that the featured var sometimes contains a String but on others it contains a Bool value. As such we need to create a custom struct to handle the decoding of this to ensure we always get a String (I am probably not doing this right way so if someone has a better solution please say so):

/// 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)

        }
    }
}

Now that is the basic structure of your JSON so now you need to handle it. In this example I am loading the JSON from the MainBundle as I dont have the actual 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)")
    }
}

In the above function you will notice I am calling the function extractDataFrom() which allows you to then do what you need to do with your data:

/// 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)
    }
}

If you wanted to save this data then all you would need to do is add the following variables under your class declaration e.g:

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

And in the extractDataFrom() function change the:

 let dailyProducts
 let featuredProducts

To:

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

Please note that this is a very crude example, and as noted, I may not be handling the 'featured' variable correctly.

Hope it helps...

BlackMirrorz
  • 7,217
  • 2
  • 20
  • 31
-1

Thanks to quicktype and other services converting valid json to Swift and other languages is simple. Editing it to fit your needs should be simple enough.

// 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)
        }
    }
}
Fabian
  • 5,040
  • 2
  • 23
  • 35