I am trying to parse JSON data from a post request and I am struggling with the outcome. I would like to parse the JSON data in the Swift 5 programming language.
I have spent hours trying to figure this out. I am very confused on how to deal with multiple levels of hiarchy like this.
I dont need all the data i just need time and total_amount_sent.
Swift
struct Response: Codable {
var txs: [ResponseData]
enum CodingKeys: String, CodingKey {
case txs
}
}
struct ResponseData: Codable {
var time: String?
var total_amount_sent: String?
var recipient: String?
var txid: String?
var amount: String?
}
let url = URL(string: paramURL)!
let json: [String: Any] = [
"key": "value"
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: json)
let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in
do {
let json = try JSONDecoder().decode(Response.self, from: data!)
print(json)
json.txs.forEach { charge in
let transaction_time = charge.time
let transaction_txid = charge.txid
let transaction_total_amount_sent = charge.total_amount_sent
print(transaction_time)
print(transaction_txid)
print(transaction_total_amount_sent)
}
} catch {
print("Error! \(error)")
}
})
task.resume()
Error:
Error! keyNotFound(CodingKeys(stringValue: "txs", intValue: nil),
Swift.DecodingError.Context(codingPath: [], debugDescription: "No
value associated with key CodingKeys(stringValue: "txs", intValue:
nil) ("txs").", underlyingError: nil))
JSON:
{
"status" : "success",
"data" : {
"network" : "DOGETEST",
"txs" : [
{
"txid" : "df4ac3ecfc356d9e10325cd4043a4103602599b71a91ea96cb3fa3204c61264c",
"from_green_address" : false,
"time" : 1620076250,
"confirmations" : 5,
"amounts_received" : [
{
"recipient" : "2N3ZbAaJqtxD5jfbLDZichV6SSHZ6PBYPmi",
"amount" : "2.00000000"
}
],
"senders" : [
"2MtSgfBmXzZv6raD7yyuGzFamzBzQD7G6Rf"
],
"confidence" : 1.0,
"propagated_by_nodes" : null
},
{
"txid" : "d983739751a6170d336016ffa1b7d7f2849a4a81cb0021e03a2cc6d76c55379d",
"from_green_address" : false,
"time" : 1620076071,
"confirmations" : 6,
"amounts_received" : [
{
"recipient" : "2N3ZbAaJqtxD5jfbLDZichV6SSHZ6PBYPmi",
"amount" : "2.00000000"
}
],
"senders" : [
"2MtSgfBmXzZv6raD7yyuGzFamzBzQD7G6Rf"
],
"confidence" : 1.0,
"propagated_by_nodes" : null
},
{
"txid" : "1dce7dd99f6a655ca82a94c8e088576c78e4ece94995f2f90891b8f5f31a54db",
"from_green_address" : false,
"time" : 1620076021,
"confirmations" : 6,
"amounts_received" : [
{
"recipient" : "2N3ZbAaJqtxD5jfbLDZichV6SSHZ6PBYPmi",
"amount" : "8.00000000"
}
],
"senders" : [
"2MtSgfBmXzZv6raD7yyuGzFamzBzQD7G6Rf"
],
"confidence" : 1.0,
"propagated_by_nodes" : null
},
{
"txid" : "27719e8fae3bb64f1ecec8ac64a2135d1ccc5d9219040dceaec9d24e4b17a466",
"from_green_address" : false,
"time" : 1620075780,
"confirmations" : 6,
"amounts_received" : [
{
"recipient" : "2N3ZbAaJqtxD5jfbLDZichV6SSHZ6PBYPmi",
"amount" : "4.00000000"
}
],
"senders" : [
"2MtSgfBmXzZv6raD7yyuGzFamzBzQD7G6Rf"
],
"confidence" : 1.0,
"propagated_by_nodes" : null
},
{
"txid" : "2a6547411b15442b78b118981cf5632e3faedec8941aea22453d8837807d37be",
"from_green_address" : false,
"time" : 1620075743,
"confirmations" : 7,
"amounts_received" : [
{
"recipient" : "2N3ZbAaJqtxD5jfbLDZichV6SSHZ6PBYPmi",
"amount" : "2.00000000"
}
],
"senders" : [
"2MtSgfBmXzZv6raD7yyuGzFamzBzQD7G6Rf"
],
"confidence" : 1.0,
"propagated_by_nodes" : null
},
{
"txid" : "63a145cbdc48c1e644fa52a30ed26d66c58b685698104a549001d0fe539816ba",
"from_green_address" : false,
"time" : 1620075697,
"confirmations" : 7,
"amounts_received" : [
{
"recipient" : "2N3ZbAaJqtxD5jfbLDZichV6SSHZ6PBYPmi",
"amount" : "4.00000000"
}
],
"senders" : [
"2MtSgfBmXzZv6raD7yyuGzFamzBzQD7G6Rf"
],
"confidence" : 1.0,
"propagated_by_nodes" : null
},
{
"txid" : "ff350583e0cb1396a3d5cc09350697daf111626ec5a3b893f8e936276f1c6715",
"from_green_address" : false,
"time" : 1620075677,
"confirmations" : 7,
"amounts_received" : [
{
"recipient" : "2N3ZbAaJqtxD5jfbLDZichV6SSHZ6PBYPmi",
"amount" : "2.00000000"
}
],
"senders" : [
"2MtSgfBmXzZv6raD7yyuGzFamzBzQD7G6Rf"
],
"confidence" : 1.0,
"propagated_by_nodes" : null
},
{
"txid" : "5a0115a9f608f3d01caee03587253fc58e0ad0c602f4e95e6c0db1e11703af84",
"from_green_address" : false,
"time" : 1619998414,
"confirmations" : 227,
"amounts_received" : [
{
"recipient" : "2N3ZbAaJqtxD5jfbLDZichV6SSHZ6PBYPmi",
"amount" : "1000.00000000"
}
],
"senders" : [
"2MytwDpHWpdoVYQ7JzYwBPxQ5XsEdiV7udZ"
],
"confidence" : 1.0,
"propagated_by_nodes" : null
}
]
}
}
Your model was not properly structured.
struct Response: Decodable {
var txs: [ResponseData]
enum CodingKeys: CodingKey {
case data
}
enum ValueCodingKeys: CodingKey {
case txs
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let nestedContainer = try container.nestedContainer(keyedBy: ValueCodingKeys.self, forKey: .data)
txs = try nestedContainer.decode([ResponseData].self, forKey: .txs)
}
}
struct ResponseData: Decodable {
var time: Int?
var txid: String?
var amountsReceived: [AmoutReceived]
}
struct AmoutReceived: Decodable {
var recipient: String?
var amount: String?
}
Use .convertFromSnakeCase.
Hope you are handling error cases properly. Here is the code
func fetch(urlString: String, callback: #escaping (Result<Response, Error>) -> Void) {
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {
if let error = error {
callback(.failure(error))
}
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Response.self, from: data)
callback(.success(result))
} catch {
callback(.failure(error))
}
}.resume()
}
You can use tools such as Quicktype.io to create a model from a JSON. This is an example model from your provided JSON. You can play around with this tool, and also there is an option "Renders output in a Swift 5 compatible mode" since you are asking about parsing in Swift 5 programming language.
import Foundation
struct User: Codable {
let status: String
let data: DataClass
}
struct DataClass: Codable {
let network: String
let txs: [Tx]
}
struct Tx: Codable {
let txid: String
let fromGreenAddress: Bool
let time, confirmations: Int
let amountsReceived: [AmountsReceived]
let senders: [String]
let confidence: Int
let propagatedByNodes: JSONNull?
enum CodingKeys: String, CodingKey {
case txid
case fromGreenAddress = "from_green_address"
case time, confirmations
case amountsReceived = "amounts_received"
case senders, confidence
case propagatedByNodes = "propagated_by_nodes"
}
}
struct AmountsReceived: Codable {
let recipient, amount: String
}
class JSONNull: Codable, Hashable {
public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
return true
}
public var hashValue: Int {
return 0
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
Related
I'm new to the Wikipedia API and I have been trying to parse the image url from the API. The JSON I'm trying to parse is as follows:
API:
https://en.wikipedia.org/w/api.php?action=query&titles=tokyo&prop=pageimages&format=json
JSON Result:
{"batchcomplete": "",
"query": {
"normalized": [
{
"from": "tokyo",
"to": "Tokyo"
}
],
"pages": {
"30057": {
"pageid": 30057,
"ns": 0,
"title": "Tokyo",
"thumbnail": {
"source": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg",
"width": 50,
"height": 27
},
"pageimage": "Skyscrapers_of_Shinjuku_2009_January.jpg"
}
}
}
}
Below are the struct I created to parse the data. I can see the url when I print it to the console, but since "source" is nested under "thumbnail", which lives in the value of the [String:Page] dict pair, I can't figure out a way to access it. How do I parse data in a dictionary like this? Thank you for your help in advance.
struct WikiAPIResults: Codable {
let batchcomplete: String?
let query: Query?
}
struct Query: Codable {
let normalized: [Normalized]?
let pages: [String:Pages]? // <- I can get to here
}
struct Normalized: Codable {
let from, to: String?
}
struct Pages: Codable {
let pageid, ns: Int?
let title: String?
let thumbnail: Thumbnail?
let pageimage: String?
}
struct Thumbnail: Codable {
let source: String? // <- But I want to grab this
let width, height: Int?
}
func fetchImageFromWikipedia(imageKeyword: String, completion: #escaping (WikiAPIResults) -> Void) {
var urlComponents = URLComponents(string: "https://en.wikipedia.org/w/api.php?")!
urlComponents.queryItems = [
"action": "query",
"titles": imageKeyword,
"prop": "pageimages",
"format": "json"].map { URLQueryItem(name: $0.key, value: $0.value) }
let task = URLSession.shared.dataTask(with: urlComponents.url!) { data, response, error in
let jsonDecoder = JSONDecoder()
if let data = data,
let result = try? jsonDecoder.decode(WikiAPIResults.self, from: data) {
completion(result)
}
}
task.resume()
}
fetchImageFromWikipedia(imageKeyword: "Tokyo") { result in
print(result.query?.pages?.values)
}
Do you mean gathering all thumbnail sources?
fetchImageFromWikipedia(imageKeyword: "Tokyo") { result in
let pages = result.query?.pages?.compactMap { $0.value } ?? []
let thumbnailSources = pages.compactMap { $0.thumbnail?.source }
print(thumbnailSources)
}
This works with your example perfectly:
import Foundation
struct Response: Decodable {
struct Query: Decodable {
struct NormalizedQuery: Decodable {
let from: String
let to: String
}
struct Page: Decodable {
struct Thumbnail: Decodable {
let source: URL
let width: Int
let height: Int
}
let id: Int
let ns: Int
let title: String
let thumbnail: Thumbnail
let pageImage: String
private enum CodingKeys: String, CodingKey {
case id = "pageid"
case ns
case title
case thumbnail
case pageImage = "pageimage"
}
}
let normalized: [NormalizedQuery]
let pages: [String : Page]
}
let batchComplete: String
let query: Query
private enum CodingKeys: String, CodingKey {
case batchComplete = "batchcomplete"
case query
}
}
let responseString = """
{
"batchcomplete" : "",
"query" : {
"normalized" : [
{
"from" : "tokyo",
"to" : "Tokyo"
}
],
"pages" : {
"30057" : {
"pageid" : 30057,
"ns" : 0,
"title" : "Tokyo",
"thumbnail" : {
"source" : "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg",
"width" : 50,
"height" : 27
},
"pageimage" : "Skyscrapers_of_Shinjuku_2009_January.jpg"
}
}
}
}
"""
let responseData = responseString.data(using: .utf8)!
let response = try! JSONDecoder().decode(Response.self, from: responseData)
print(response)
// Response(batchComplete: "", query: __lldb_expr_18.Response.Query(normalized: [__lldb_expr_18.Response.Query.NormalizedQuery(from: "tokyo", to: "Tokyo")], pages: ["30057": __lldb_expr_18.Response.Query.Page(id: 30057, ns: 0, title: "Tokyo", thumbnail: __lldb_expr_18.Response.Query.Page.Thumbnail(source: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg, width: 50, height: 27), pageImage: "Skyscrapers_of_Shinjuku_2009_January.jpg")]))
i am trying this but getting error . whats other way to sort this list of items .
getting error Value of tuple type
'(String, JSON)' has no member 'subscript'
let jsonData = try JSONSerialization.data(withJSONObject: bizSnapshot.value as Any, options: .prettyPrinted)
var bizOfferingsJson = try JSON(data: jsonData)
bizOfferingsJson items contains itemIndex as key as Int , i want to sort based on that itemIndex
bizOfferingsJson = bizOfferingsJson.sorted(by: { $0.["itemIndex"] > $1.["itemIndex"] })
print("bizOfferingsJson===",bizOfferingsJson)
sample bizOfferingsJson data
[
{
"isVisible" : true,
"itemRetailInfo" : {
"units" : "GRAMS",
"quantityPrice" : [
{
"discountPrice" : 0,
"regularPrice" : 25,
"quantity" : 1000,
"quantitySelected" : 1000,
"isEditable" : false
}
]
},
"imageStoragePathList" : [
"ONION_RED.png"
],
"bizOfferingsID" : "Vegetables_0",
"masterImage" : 0,
"vegNonVegInfoEnum" : "NONE",
"description" : "",
"itemQunatitySelected" : 0,
"bizOfferingsHeaderTypeThree" : {
"indexOrder" : 0,
"headerText" : "Vegetables",
"isVisible" : true
},
"itemIndex" : 0,
"totalCountQunatity" : 0,
"primaryText" : "Onion Red"
},
{
"isVisible" : true,
"itemRetailInfo" : {
"units" : "GRAMS",
"quantityPrice" : [
{
"discountPrice" : 0,
"regularPrice" : 25,
"quantity" : 1000,
"quantitySelected" : 1000,
"isEditable" : false
}
]
},
"imageStoragePathList" : [
"POTATO.png"
],
"bizOfferingsID" : "Vegetables_1",
"masterImage" : 0,
"vegNonVegInfoEnum" : "NONE",
"description" : "",
"itemQunatitySelected" : 0,
"bizOfferingsHeaderTypeThree" : {
"indexOrder" : 0,
"headerText" : "Vegetables",
"isVisible" : true
},
"itemIndex" : 1,
"totalCountQunatity" : 0,
"primaryText" : "Potato"
},
{
"isVisible" : true,
"itemRetailInfo" : {
"units" : "PIECES",
"quantityPrice" : [
{
"discountPrice" : 0,
"regularPrice" : 3,
"quantity" : 1,
"quantitySelected" : 0.10000000000000001,
"isEditable" : false
}
]
},
"imageStoragePathList" : [
"CORIANDER_LEAVES_BUNCH.png"
],
"bizOfferingsID" : "Vegetables_2",
"masterImage" : 0,
"vegNonVegInfoEnum" : "NONE",
"description" : "",
"itemQunatitySelected" : 0,
"bizOfferingsHeaderTypeThree" : {
"indexOrder" : 0,
"headerText" : "Vegetables",
"isVisible" : true
},
"itemIndex" : 2,
"totalCountQunatity" : 0,
"primaryText" : "Coriander Leaves Bunch"
},
{
"isVisible" : true,
"itemRetailInfo" : {
"units" : "GRAMS",
"quantityPrice" : [
{
"discountPrice" : 0,
"regularPrice" : 8,
"quantity" : 500,
"quantitySelected" : 500,
"isEditable" : false
}
]
},
"imageStoragePathList" : [
"TOMATO_STANDARD.png"
],
"bizOfferingsID" : "Vegetables_3",
"masterImage" : 0,
"vegNonVegInfoEnum" : "NONE",
"description" : "",
"itemQunatitySelected" : 0,
"bizOfferingsHeaderTypeThree" : {
"indexOrder" : 0,
"headerText" : "Vegetables",
"isVisible" : true
},
"itemIndex" : 3,
"totalCountQunatity" : 0,
"primaryText" : "Tomato Standard"
}
]
You need to clarify which type you sort assuming an array
var bizOfferingsJson = try JSON(data: jsonData).array!
bizOfferingsJson = bizOfferingsJson.sorted(by: { $0["itemIndex"] > $1["itemIndex"] })
or using mutating sort
bizOfferingsJson.sort{ $0["itemIndex"] > $1["itemIndex"] }
using Codable is much better given your json
struct Root: Codable {
let isVisible: Bool
let itemRetailInfo: ItemRetailInfo
let imageStoragePathList: [String]
let bizOfferingsID: String
let masterImage: Int
let vegNonVegInfoEnum, purpleDescription: String
let itemQunatitySelected: Int
let bizOfferingsHeaderTypeThree: BizOfferingsHeaderTypeThree
let itemIndex, totalCountQunatity: Int
let primaryText: String
enum CodingKeys: String, CodingKey {
case isVisible, itemRetailInfo, imageStoragePathList, bizOfferingsID, masterImage, vegNonVegInfoEnum
case purpleDescription = "description"
case itemQunatitySelected, bizOfferingsHeaderTypeThree, itemIndex, totalCountQunatity, primaryText
}
}
// MARK: - BizOfferingsHeaderTypeThree
struct BizOfferingsHeaderTypeThree: Codable {
let indexOrder: Int
let headerText: String
let isVisible: Bool
}
// MARK: - ItemRetailInfo
struct ItemRetailInfo: Codable {
let units: String
let quantityPrice: [QuantityPrice]
}
// MARK: - QuantityPrice
struct QuantityPrice: Codable {
let discountPrice, regularPrice, quantity: Int
let quantitySelected: Double
let isEditable: Bool
}
var res = try! JSONDecoder().decode([Root].self, from: jsonData)
res.sort { $0.itemIndex > $1.itemIndex }
I am having difficulties parsing data (JSON-like I get from endpoint) to Swift structs. As it seems data I get from the endpoint is not a valid JSON (at least not all of it looking at the structure of object = (...)), so I can't decode ListStruct.
Should I parse it another way? Any advice much appreciated
Structs I prepared are:
struct Response:Codable {
let message:String?
let list:ListStruct?
let error:Bool?
}
struct ListStruct:Codable {
let object1:[Object1]?
let object2:[Object2]?
let object3:[Object3]?
}
struct Object1:Codable {
id:Int?
name:String?
}
...
Example of data I get from endpoint:
["message": <null>, "list": {
object1 = (
{
id = 1;
name = "testing1";
}
);
object2 = (
{
files = (
);
id = 1;
name = "testing2-1";
photos = (
);
},
{
files = (
);
id = 2;
name = "testing2-2";
photos = (
);
systemId = 8;
}
);
object3 = (
{
id = 6;
name = "testing3-1";
},
{
id = 13;
name = "testing3-2";
}
);
}, "error": 0]
EDIT
How I am trying do decode:
if let result = try JSONDecoder().decode(Response?.self, from: "\(response!)".data(using: .utf8)! ) {
print("\(result)")
}
Error I am getting:
Error: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No string key for value in object around character 6." UserInfo={NSDebugDescription=No string key for value in object around character 6.})))
Most probably, you are passing the wrong data object by creating using string interpolation. If the response type is Data then you don't need to recreate it again in the below line,
if let result = try JSONDecoder().decode(Response?.self, from: "\(response!)".data(using: .utf8)! ) {
Try to pass the response as it is. Shown below,
if let result = try JSONDecoder().decode(Response?.self, from: response!) {
Here is a complete testable example where the correct data object is created using the json in question and error type in Response is changed from Optional Bool to Int,
struct Response:Codable {
let message:String?
let list:ListStruct?
let error: Int?
}
struct ListStruct: Codable {
let object1:[Object1]?
let object2:[Object2]?
let object3:[Object3]?
}
struct Object1: Codable {
var id:Int?
var name:String?
}
struct Object2: Codable {
var id:Int?
var name:String?
var systemId: Int?
}
struct Object3: Codable {
var id:Int?
var name:String?
}
Usage:
let data = """
{"message": null,
"list": {
"object1": [
{
"id": 1,
"name": "testing1"
}
],
"object2" : [
{
"files" : [
],
"id" : 1,
"name" : "testing2-1",
"photos" : [
]
},
{
"files" : [
],
"id" : 2,
"name" : "testing2-2",
"photos" : [
],
"systemId" : 8
}
],
"object3" : [
{
"id" : 6,
"name" : "testing3-1",
},
{
"id" : 13,
"name" : "testing3-2",
}
]
},
"error": 0
}
""".data(using: .utf8)!
if let result = try! JSONDecoder().decode(Response?.self, from: data) {
result.list?.object1?.forEach({ obj in
print(obj.name)
})
result.list?.object2?.forEach({ obj in
print(obj.name)
})
result.list?.object3?.forEach({ obj in
print(obj.name)
})
}
Output:
Optional("testing1")
Optional("testing2-1")
Optional("testing2-2")
Optional("testing3-1")
Optional("testing3-2")
I'm making multiple calls to the Apple Music API which is returning an array of Resource objects. The Resource object has a property called attributes of type object which varies depending on whether it is a Song, Playlist, or Album.
How do I model the resource object so I can decode the JSON and create Song, Playlist, and Album objects?
struct Resource {
let id : String?
let type : String?
let href : String?
let attributes : "What goes here?"
}
Update 1:
I tried Joe's second recommendation and set up my structs like so:
struct Resource: Decodable{
enum Attribute {
case song(Song), playlist(Playlist)
}
let id : String?
let type : String?
let href : String?
let attributes: Attribute?
enum CodingKeys : CodingKey{
case id, type, href, attributes
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
type = try values.decode(String.self, forKey: .type)
href = try values.decode(String.self, forKey: .href)
switch type {
case "library-songs":
attributes = try .song(values.decode(Song.self, forKey: .attributes))
case "library-playlists":
attributes = try .playlist(values.decode(Playlist.self, forKey: .attributes))
default:
attributes = nil
}
}
}
struct Song : Decodable {
let playParams : PlayParams?
let trackNumber : Int?
let durationInMillis : Int?
let name : String?
let albumName : String?
let artwork : Artwork?
let contentRating : String?
let artistName : String?
}
struct Artwork : Decodable {
let width : Int?
let height : Int?
let url : String?
}
struct PlayParams : Decodable {
let id : String?
let kind : String?
let isLibrary : Bool?
}
The sample JSON looks like this for song:
{
"data": [
{
"id": "i.4YBNbl3IXVJQRM",
"type": "library-songs",
"href": "/v1/me/library/songs/i.4YBNbl3IXVJQRM",
"attributes": {
"albumName": "\"Awaken, My Love!\"",
"artwork": {
"width": 1200,
"height": 1200,
"url": "https://is5-ssl.mzstatic.com/image/thumb/Music71/v4/00/d0/d7/00d0d743-b0de-31d8-09eb-0796269bb555/UMG_cvrart_00044003187658_01_RGB72_1800x1800_16UMGIM77118.jpg/{w}x{h}bb.jpg"
},
"durationInMillis": 326933,
"playParams": {
"id": "i.4YBNbl3IXVJQRM",
"kind": "song",
"isLibrary": true
},
"artistName": "Childish Gambino",
"trackNumber": 6,
"name": "Redbone",
"contentRating": "explicit"
}
},
{
"id": "i.mmpeOrZiLqoKOv",
"type": "library-songs",
"href": "/v1/me/library/songs/i.mmpeOrZiLqoKOv",
"attributes": {
"albumName": "Funk Wav Bounces Vol. 1",
"artwork": {
"width": 1200,
"height": 1200,
"url": "https://is5-ssl.mzstatic.com/image/thumb/Music127/v4/8b/37/23/8b372308-f764-d03a-5bda-a7a456292547/886446469607.jpg/{w}x{h}bb.jpg"
},
"durationInMillis": 272659,
"playParams": {
"id": "i.mmpeOrZiLqoKOv",
"kind": "song",
"isLibrary": true
},
"artistName": "Calvin Harris",
"trackNumber": 4,
"name": "Rollin (feat. Future & Khalid)",
"contentRating": "explicit"
}
},
{
"id": "i.JL1aVxNtzmYDJG",
"type": "library-songs",
"href": "/v1/me/library/songs/i.JL1aVxNtzmYDJG",
"attributes": {
"albumName": "The Weekend (Funk Wav Remix) - Single",
"artwork": {
"width": 1200,
"height": 1200,
"url": "https://is4-ssl.mzstatic.com/image/thumb/Music118/v4/aa/d5/e5/aad5e5e3-dff5-7d8f-5747-b695ad9f2299/886446852157.jpg/{w}x{h}bb.jpg"
},
"durationInMillis": 171806,
"playParams": {
"id": "i.JL1aVxNtzmYDJG",
"kind": "song",
"isLibrary": true
},
"artistName": "SZA & Calvin Harris",
"trackNumber": 1,
"name": "The Weekend (Funk Wav Remix)"
}
}
]
}
When I decode the data using:
let resource = try? JSONDecoder().decode([Resource].self, from: data!)
print(resource!)
I get a fatal error: Unexpectedly found nil while unwrapping an Optional value
Update 2:
I figured out the issue. The extra layer on top was screwing up the decoding. I added another level:
struct Welcome: Codable {
let data: [Datum]
}
struct Datum: Codable {
let id, type, href: String
let attributes: Attributes
}
struct Attributes: Codable {
let albumName: String
let artwork: Artwork
let durationInMillis: Int
let playParams: PlayParams
let artistName: String
let trackNumber: Int
let name: String
let contentRating: String?
}
struct Artwork: Codable {
let width, height: Int
let url: String
}
struct PlayParams: Codable {
let id, kind: String
let isLibrary: Bool
}
This just works for songs but I will try reimplementing for both songs and playlists.
I watch WWDC session 102 and I try to use JSONDecoder to model,but I have a question,this is my struct
public struct DataListResult:Codable{
let _id : String
let createdAt : String
let desc : String
let images : Array<String>
let publishedAt : String
let source : String
let type : String
let url : String
let used : Int
let who : String
}
public struct DataListModel:Codable{
let results : [DataListResult]
let error: Bool
}
JSON:
{
"results" : [
{
"_id" : "59266a79421aa92c73b6475c",
"images" : [
"http:\/\/img.gank.io\/875a9508-3a1e-4d4b-8b91-c111ea62871a"
],
"source" : "chrome",
"who" : "S",
"publishedAt" : "2017-05-25T13:32:48.92Z",
"used" : true,
"createdAt" : "2017-05-25T13:24:09.35Z",
"type" : "iOS",
"desc" : "iOS ",
"url" : "https:\/\/github.com\/adamzjk\/iOS-ObjectDetection"
},
{
"_id" : "592502d6421aa92c769a8bac",
"images" : [
"http:\/\/img.gank.io\/44e8aa0a-b66f-4a5b-9cb0-74c3ae9fc156"
],
"source" : "chrome",
"who" : "S",
"publishedAt" : "2017-05-24T12:09:25.526Z",
"used" : true,
"createdAt" : "2017-05-24T11:49:42.14Z",
"type" : "iOS",
"desc" : "Whatʼs new in Swift 4",
"url" : "https:\/\/github.com\/ole\/whats-new-in-swift-4"
},
{
"_id" : "5923a438421aa92c73b64743",
"images" : [
"http:\/\/img.gank.io\/25762b53-b2ba-4c1c-9666-1683cd72bb82"
],
"source" : "chrome",
"who" : "who",
"publishedAt" : "2017-05-23T11:14:05.141Z",
"used" : true,
"createdAt" : "2017-05-23T10:53:44.853Z",
"type" : "iOS",
"desc" : "music",
"url" : "https:\/\/github.com\/HarshilShah\/DeckTransition"
}
],
"error" : false
}
The property images can't decoded,because I remove this it can be success,but I don't know how to fix,please tell me, thank you
do
{
let model = try JSONDecoder().decode(DataListModel.self, from: response.data!)
}catch let error as NSError {
print("\(error)") //Error Domain=Swift.DecodingError Code=2 "(null)"
}
--------- last update 2017.6.17 ----------
let images : Array<String> -> let images : Array<String>?
images need opotional property when it empty,thank you for your help
The property used is supposed to be Bool but the structure is decoded correctly with
do {
let decoded = try JSONDecoder().decode(DataListModel.self, from: data)
print(decoded)
} catch {
print(error)
}
assuming data is the JSON Data object.