Reputation: 99
I am having difficulties parsing a JSON.
URL I'm using to pull this JSON below is: https://api.flickr.com/services/rest/?method=flickr.photos.getSizes&api_key=f5963392b48503b5e16b85a3cb31cf31&photo_id=46532317604&format=json&nojsoncallback=1
{
"sizes": {
"canblog": 0,
"canprint": 0,
"candownload": 1,
"size": [
{
"label": "Square",
"width": 75,
"height": 75,
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_s.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/sq/",
"media": "photo"
},
{
"label": "Large Square",
"width": "150",
"height": "150",
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_q.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/q/",
"media": "photo"
},
{
"label": "Thumbnail",
"width": "100",
"height": "77",
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_t.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/t/",
"media": "photo"
},
{
"label": "Small",
"width": "240",
"height": "184",
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_m.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/s/",
"media": "photo"
},
{
"label": "Small 320",
"width": "320",
"height": 245,
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_n.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/n/",
"media": "photo"
},
{
"label": "Medium",
"width": "500",
"height": "383",
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/m/",
"media": "photo"
},
{
"label": "Medium 640",
"width": "640",
"height": "490",
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_z.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/z/",
"media": "photo"
},
{
"label": "Medium 800",
"width": "800",
"height": 613,
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_c.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/c/",
"media": "photo"
},
{
"label": "Large",
"width": "1024",
"height": "784",
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_b.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/l/",
"media": "photo"
},
{
"label": "Original",
"width": "1280",
"height": "980",
"source": "https://farm8.staticflickr.com/7896/46532317604_e04977f75e_o.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/o/",
"media": "photo"
}
]
},
"stat": "ok"
}
When I declare height & width variable to be String, I get prompted that a number was found instead. When I declare height & width as Int or Double, I get message that string/data was found instead. For testing purposes, I declared type as Height and Width as Bool, I received error that number was found instead.
I don't care about the height and width and if it was possible to set these variable to Any or AnyObject and/or discard them, I'd accept it. However, I'm not finding any example of Any or AnyObject Type working with Codable Struct. Should I be considering converting my code to utilize JSONSerialization?
My current Code is below:
class func getPhotoSizeWithURL(photoId: String, completion: @escaping (String?, Error?)-> Void){
let url = Endpoints.getPhotosGetSizes(photoId).url
let task = URLSession.shared.dataTask(with: url) { (data, response, err) in
print("\n\n\nGoing to URLSession with --> \(url)")
if let error = err {
DispatchQueue.main.async {
completion(nil, error)
}
return
}
guard let data = data else {
DispatchQueue.main.async {
completion(nil, err)
}
return
}
do {
let dataObject = try JSONDecoder().decode(PhotosGetSizes.self, from: data)
print("GET-SIZES ---> \(dataObject.sizes.size.last?.url ?? "")")
DispatchQueue.main.async {
completion(dataObject.sizes.size.first?.url, nil)
}
return
} catch let conversionErr {
print("\(conversionErr.localizedDescription)\n\n\(conversionErr)")
DispatchQueue.main.async {
completion(nil, conversionErr)
}
return
}
}
task.resume()
}
Upvotes: 0
Views: 132
Reputation: 794
As evident from the JSON you provided height and width are sometimes returned as strings and sometimes returned as numbers by the API you are calling:
"size": [
{
"label": "Square",
"width": 75, // <-- number
"height": 75, // <-- number
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_s.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/sq/",
"media": "photo"
},
{
"label": "Large Square",
"width": "150", // <-- string
"height": "150", // <-- string
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_q.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/q/",
"media": "photo"
},
...
{
"label": "Small 320",
"width": "320", // <-- string
"height": 245, // <-- number... really?
"source": "https://farm8.staticflickr.com/7896/46532317604_61fbd5812b_n.jpg",
"url": "https://www.flickr.com/photos/72616463@N00/46532317604/sizes/n/",
"media": "photo"
},
...
]
Of course JavaScript is the odd sibling who doesn't care about types that much, at least compared to Swift which is strongly typed.
You say that you don't care about the height and width properties and ask if there is a way to discard them:
Yes! Simply leave them out of your Codable type and no attempt will be made to deserialise them.
Or, sob silently as you face the reality of numbers returned as strings and create a custom Codable type for sizes that can be initialised from either strings or numbers, as Sh_Khan suggested.
Upvotes: 1
Reputation: 100503
You can try an enum
of the width and height for your current response it's now either String
/ Int
check and add any other possibility
struct Root: Codable {
let sizes: Sizes
let stat: String
}
struct Sizes: Codable {
let canblog, canprint, candownload: Int
let size: [Size]
}
struct Size: Codable {
let label,media,source: String
let width, height: Height
let url:URL
}
enum Height: Codable {
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(Height.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Height"))
}
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)
}
}
}
Upvotes: 1