How to deserialize JSON in swift - json

I want to decode a JSON file into model objects. Unfortunately it doesn't work in the right way. So I don't get errors, but the "decoding-result" don't corresponds to my expectations.
I have the following JSON file and I want to decode it in the shown structs. I have trimmed the json file. Why get I just one "slider image" instead of 5 (the property image of ImagesSlider contains an array with just the first image/element).
What am I missing?
JSON:
[{"imageSlider" : [{
"image" : [{
"imageId" : "1",
"imageName" : "germany1",
"imageBigName" : "germany1_BIG",
"imageRights" : "Peter"
}],
"image" : [{
"imageId" : "2",
"imageName" : "germany2",
"imageBigName" : "germany2_BIG",
"imageRights" : "Peter"
}],
"image" : [{
"imageId" : "3",
"imageName" : "germany3",
"imageBigName" : "germany3_BIG",
"imageRights" : "Peter"
}],
"image" : [{
"imageId" : "4",
"imageName" : "germany4",
"imageBigName" : "germany4_BIG",
"imageRights" : "Peter"
}],
"image" : [{
"imageId" : "5",
"imageName" : "germany5",
"imageBigName" : "germany5_BIG",
"imageRights" : "Peter"
}]
}]
}]
Swift:
struct CountryModel : Decodable, Equatable {
var countryName : String
var inhabitants : String
var capital : String
var currency : String
var imageName : String
var imageSlider: [ImagesSlider]
}
struct ImagesSlider : Decodable, Equatable {
var image: [Image]
}
struct Image : Decodable, Equatable {
var imageId: String
var imageName: String
var imageBigName: String
var imageRights: String
}
Decoding:
func loadData() -> [CountryModel] {
var data: Data
guard let file = Bundle.main.url(forResource: "data", withExtension: "json") else {
fatalError("Error")
}
data = try! Data(contentsOf: file)
let decoder = JSONDecoder()
return try! decoder.decode([CountryModel].self, from: data)
}
Thanks for your help...
Edit:
My question isn't solved by the linked question...

The json looks wrong, remember that you should have only one unique key per json object. Right now is:
[
{
"imageSlider": [
{
"image": [
{
"imageId": "1",
"imageName": "germany1",
"imageBigName": "germany1_BIG",
"imageRights": "Peter"
}
],
"image": [
{
"imageId": "2",
"imageName": "germany2",
"imageBigName": "germany2_BIG",
"imageRights": "Peter"
}
]
}
]
}
]
And it should be like this:
[
{
"imageSlider": [
{
"image": [
{
"imageId": "1",
"imageName": "germany1",
"imageBigName": "germany1_BIG",
"imageRights": "Peter"
},
{
"imageId": "2",
"imageName": "germany2",
"imageBigName": "germany2_BIG",
"imageRights": "Peter"
}
]
}
]
}
]
Notice that "germany2" item is inside of the array of image.
let decoder = JSONDecoder()
let jsonData = Data(jsonString.utf8)
do {
let country = try decoder.decode([CountryModel].self, from: jsonData)
print(country[0].imageSlider[0].image.count)
} catch {
print(error.localizedDescription)
}
// Console message
2

Because your JSON contains multiple Images object with the same key! That is not valid and each one overwrites others (randomly probably). You should turn that json to array of objects:
[{
"imageSlider" : [{
"image" : [{
"imageId" : "1",
"imageName" : "germany1",
"imageBigName" : "germany1_BIG",
"imageRights" : "Peter"
}]},{
"image" : [{
"imageId" : "2",
"imageName" : "germany2",
"imageBigName" : "germany2_BIG",
"imageRights" : "Peter"
}]}
}]
}]
Also other keys of the CountryModel should be presented since all of them are non-optional.

Related

Golang proper way to send json response with status

How to send json response with status code in response body.
My code
func getUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var user []User
result := db.Find(&user)
json.NewEncoder(w).Encode(result)
}
My result now:
[
{
"name" : "test",
"age" : "28",
"email":"test#gmail.com"
},
{
"name" : "sss",
"age" : "60",
"email":"ss#gmail.com"
},
{
"name" : "ddd",
"age" : "30",
"email":"ddd#gmail.com"
},
]
But I need to send the response with status code like this
{
status : "success",
statusCode : 200,
data : [
{
"name" : "test",
"age" : "28",
"email":"test#gmail.com"
},
{
"name" : "sss",
"age" : "60",
"email":"ss#gmail.com"
},
{
"name" : "ddd",
"age" : "30",
"email":"ddd#gmail.com"
},
]
}
If you want different json, pass a different object to Encode.
type Response struct {
Status string `json:"status"`
StatucCode int `json:"statusCode"`
Data []User `json:"data"`
}
func getUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var user []User
result := db.Find(&user)
json.NewEncoder(w).Encode(&Response{"success", 200, result})
}
Or use a map:
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "success",
"statusCode": 200,
"data": result,
})

How can I decode two different response JSON in one request?

I am trying to get response from Google's Book API. In there I can get response but for example, in some books have description or many other keys in response JSON. If I desing my structs according to with descriptions key, other books which not have description gave me dataCorrupted error and not decoding JSON. If I do otherwise, I get the same error on other books which have description. I hope I can explain myself. How can I solve it correctly?
Here my root class codable:
import Foundation
struct RootClass : Codable {
let items : [Item]?
let kind : String?
let totalItems : Int?
enum CodingKeys: String, CodingKey {
case items = "items"
case kind = "kind"
case totalItems = "totalItems"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
items = try values.decodeIfPresent([Item].self, forKey: .items)
kind = try values.decodeIfPresent(String.self, forKey: .kind)
totalItems = try values.decodeIfPresent(Int.self, forKey: .totalItems)
}
}
I need to reach root -> items -> volumeInfo.
And here Item codable struct:
import Foundation
struct Item : Codable {
let accessInfo : AccessInfo?
let etag : String?
let id : String?
let kind : String?
let saleInfo : SaleInfo?
let searchInfo : SearchInfo?
let selfLink : String?
let volumeInfo : VolumeInfo?
enum CodingKeys: String, CodingKey {
case accessInfo = "accessInfo"
case etag = "etag"
case id = "id"
case kind = "kind"
case saleInfo = "saleInfo"
case searchInfo = "searchInfo"
case selfLink = "selfLink"
case volumeInfo = "volumeInfo"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
accessInfo = try AccessInfo(from: decoder)
etag = try values.decodeIfPresent(String.self, forKey: .etag)
id = try values.decodeIfPresent(String.self, forKey: .id)
kind = try values.decodeIfPresent(String.self, forKey: .kind)
saleInfo = try SaleInfo(from: decoder)
searchInfo = try SearchInfo(from: decoder)
selfLink = try values.decodeIfPresent(String.self, forKey: .selfLink)
volumeInfo = try VolumeInfo(from: decoder)
}
}
And Here I parse json :
func parseJSON(searchData: Data) -> [SearchedModel]?{
let decode = JSONDecoder()
do{
let decodedData = try decode.decode(RootClass.self, from: searchData)
// IN HERE I CAN USE FOR LOOP TO ITEMS AND GET VOLUME INFO DATAS BUT ITS ALL NIL
return mainDataArr
}catch{
print(error)
return nil
}
}
When I try to loop over to items and get volume info datas its all nil. I can't understand why is this all nil? How can I solve it?
Here part of JSON 1 :
"kind": "books#volumes",
"totalItems": 1196,
"items": [
{
"kind": "books#volume",
"id": "4qnmsgEACAAJ",
"etag": "dYXW8bT6JB0",
"selfLink": "https://www.googleapis.com/books/v1/volumes/4qnmsgEACAAJ",
"volumeInfo": {
"title": "Sineklerin Tanrisi",
"authors": [
"William Golding"
],
"publishedDate": "2014-01-01",
"industryIdentifiers": [
{
"type": "ISBN_10",
"identifier": "9754582904"
},
{
"type": "ISBN_13",
"identifier": "9789754582901"
}
],
"readingModes": {
"text": false,
"image": false
},
"pageCount": 261,
"printType": "BOOK",
"categories": [
"Boys"
],
"maturityRating": "NOT_MATURE",
"allowAnonLogging": false,
"contentVersion": "preview-1.0.0",
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=4qnmsgEACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=4qnmsgEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
},
"language": "tr",
"previewLink": "http://books.google.com.tr/books?id=4qnmsgEACAAJ&dq=sineklerin+tanrisi&hl=&cd=1&source=gbs_api",
"infoLink": "http://books.google.com.tr/books?id=4qnmsgEACAAJ&dq=sineklerin+tanrisi&hl=&source=gbs_api",
"canonicalVolumeLink": "https://books.google.com/books/about/Sineklerin_Tanrisi.html?hl=&id=4qnmsgEACAAJ"
},
"saleInfo": {
"country": "TR",
"saleability": "NOT_FOR_SALE",
"isEbook": false
},
"accessInfo": {
"country": "TR",
"viewability": "NO_PAGES",
"embeddable": false,
"publicDomain": false,
"textToSpeechPermission": "ALLOWED",
"epub": {
"isAvailable": false
},
"pdf": {
"isAvailable": false
},
"webReaderLink": "http://play.google.com/books/reader?id=4qnmsgEACAAJ&hl=&printsec=frontcover&source=gbs_api",
"accessViewStatus": "NONE",
"quoteSharingAllowed": false
}
}
And here JSON 2 (with same request's response):
"kind": "books#volumes",
"totalItems": 432,
"items": [
{
"kind": "books#volume",
"id": "yodWha1LmPsC",
"etag": "2bZz2CDqiq4",
"selfLink": "https://www.googleapis.com/books/v1/volumes/yodWha1LmPsC",
"volumeInfo": {
"title": "Momo the Monkey Arrives",
"authors": [
"Shariffa Keshavjee"
],
"publisher": "Master Publishing",
"publishedDate": "2012-09-21",
"description": "Momo the Monkey Arrives is the first in a series of illustrated children's books about the rescue of a monkey and how his presence livens up the household of two children: a boy and a girl. Along the way, as they take care of Momo, Geno and Alid learn how to give the best possible care to a monkey like him, and how to be more responsible children. The adventures of Momo are based on a true story.",
"industryIdentifiers": [
{
"type": "ISBN_13",
"identifier": "9789966158987"
},
{
"type": "ISBN_10",
"identifier": "9966158987"
}
],
"readingModes": {
"text": true,
"image": true
},
"pageCount": 28,
"printType": "BOOK",
"categories": [
"Juvenile Fiction"
],
"maturityRating": "NOT_MATURE",
"allowAnonLogging": false,
"contentVersion": "1.4.4.0.preview.3",
"panelizationSummary": {
"containsEpubBubbles": false,
"containsImageBubbles": false
},
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=yodWha1LmPsC&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=yodWha1LmPsC&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"
},
"language": "en",
"previewLink": "http://books.google.com.tr/books?id=yodWha1LmPsC&pg=PT25&dq=momo&hl=&cd=1&source=gbs_api",
"infoLink": "https://play.google.com/store/books/details?id=yodWha1LmPsC&source=gbs_api",
"canonicalVolumeLink": "https://play.google.com/store/books/details?id=yodWha1LmPsC"
},
"saleInfo": {
"country": "TR",
"saleability": "FOR_SALE",
"isEbook": true,
"listPrice": {
"amount": 35.04,
"currencyCode": "TRY"
},
"retailPrice": {
"amount": 35.04,
"currencyCode": "TRY"
},
"buyLink": "https://play.google.com/store/books/details?id=yodWha1LmPsC&rdid=book-yodWha1LmPsC&rdot=1&source=gbs_api",
"offers": [
{
"finskyOfferType": 1,
"listPrice": {
"amountInMicros": 35040000,
"currencyCode": "TRY"
},
"retailPrice": {
"amountInMicros": 35040000,
"currencyCode": "TRY"
}
}
]
},
"accessInfo": {
"country": "TR",
"viewability": "PARTIAL",
"embeddable": true,
"publicDomain": false,
"textToSpeechPermission": "ALLOWED",
"epub": {
"isAvailable": true
},
"pdf": {
"isAvailable": true
},
"webReaderLink": "http://play.google.com/books/reader?id=yodWha1LmPsC&hl=&printsec=frontcover&source=gbs_api",
"accessViewStatus": "SAMPLE",
"quoteSharingAllowed": false
},
"searchInfo": {
"textSnippet": "<b>Momo</b> the Monkey Adventure Series <b>Momo</b> the Monkey Arrives <b>Momo</b> Makes a <br>\nMess <b>Momo</b> Comes to the Rescue <b>Momo</b> Moves to the Orphanage Look out for <br>\nthese stories about <b>Momo</b> and his friends. More coming soon! Master Publishing<br>\n ..."
}
}
The "data corrupted" error seems to be due to the presence of the \n in the textSnippet. If we take care of that manually, I can't reproduce any issue. Here is a complete (unlike your code) example that compiles and runs correctly in an iOS project, using the JSON you provided (patched up to be valid JSON):
let json1 = """
{
"kind": "books#volumes",
"totalItems": 1196,
"items": [
{
"kind": "books#volume",
"id": "4qnmsgEACAAJ",
"etag": "dYXW8bT6JB0",
"selfLink": "https://www.googleapis.com/books/v1/volumes/4qnmsgEACAAJ",
"volumeInfo": {
"title": "Sineklerin Tanrisi",
"authors": [
"William Golding"
],
"publishedDate": "2014-01-01",
"industryIdentifiers": [
{
"type": "ISBN_10",
"identifier": "9754582904"
},
{
"type": "ISBN_13",
"identifier": "9789754582901"
}
],
"readingModes": {
"text": false,
"image": false
},
"pageCount": 261,
"printType": "BOOK",
"categories": [
"Boys"
],
"maturityRating": "NOT_MATURE",
"allowAnonLogging": false,
"contentVersion": "preview-1.0.0",
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=4qnmsgEACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=4qnmsgEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
},
"language": "tr",
"previewLink": "http://books.google.com.tr/books?id=4qnmsgEACAAJ&dq=sineklerin+tanrisi&hl=&cd=1&source=gbs_api",
"infoLink": "http://books.google.com.tr/books?id=4qnmsgEACAAJ&dq=sineklerin+tanrisi&hl=&source=gbs_api",
"canonicalVolumeLink": "https://books.google.com/books/about/Sineklerin_Tanrisi.html?hl=&id=4qnmsgEACAAJ"
},
"saleInfo": {
"country": "TR",
"saleability": "NOT_FOR_SALE",
"isEbook": false
},
"accessInfo": {
"country": "TR",
"viewability": "NO_PAGES",
"embeddable": false,
"publicDomain": false,
"textToSpeechPermission": "ALLOWED",
"epub": {
"isAvailable": false
},
"pdf": {
"isAvailable": false
},
"webReaderLink": "http://play.google.com/books/reader?id=4qnmsgEACAAJ&hl=&printsec=frontcover&source=gbs_api",
"accessViewStatus": "NONE",
"quoteSharingAllowed": false
}
}
]
}
"""
let json2 = """
{
"kind": "books#volumes",
"totalItems": 432,
"items": [
{
"kind": "books#volume",
"id": "yodWha1LmPsC",
"etag": "2bZz2CDqiq4",
"selfLink": "https://www.googleapis.com/books/v1/volumes/yodWha1LmPsC",
"volumeInfo": {
"title": "Momo the Monkey Arrives",
"authors": [
"Shariffa Keshavjee"
],
"publisher": "Master Publishing",
"publishedDate": "2012-09-21",
"description": "Momo the Monkey Arrives is the first in a series of illustrated children's books about the rescue of a monkey and how his presence livens up the household of two children: a boy and a girl. Along the way, as they take care of Momo, Geno and Alid learn how to give the best possible care to a monkey like him, and how to be more responsible children. The adventures of Momo are based on a true story.",
"industryIdentifiers": [
{
"type": "ISBN_13",
"identifier": "9789966158987"
},
{
"type": "ISBN_10",
"identifier": "9966158987"
}
],
"readingModes": {
"text": true,
"image": true
},
"pageCount": 28,
"printType": "BOOK",
"categories": [
"Juvenile Fiction"
],
"maturityRating": "NOT_MATURE",
"allowAnonLogging": false,
"contentVersion": "1.4.4.0.preview.3",
"panelizationSummary": {
"containsEpubBubbles": false,
"containsImageBubbles": false
},
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=yodWha1LmPsC&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=yodWha1LmPsC&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"
},
"language": "en",
"previewLink": "http://books.google.com.tr/books?id=yodWha1LmPsC&pg=PT25&dq=momo&hl=&cd=1&source=gbs_api",
"infoLink": "https://play.google.com/store/books/details?id=yodWha1LmPsC&source=gbs_api",
"canonicalVolumeLink": "https://play.google.com/store/books/details?id=yodWha1LmPsC"
},
"saleInfo": {
"country": "TR",
"saleability": "FOR_SALE",
"isEbook": true,
"listPrice": {
"amount": 35.04,
"currencyCode": "TRY"
},
"retailPrice": {
"amount": 35.04,
"currencyCode": "TRY"
},
"buyLink": "https://play.google.com/store/books/details?id=yodWha1LmPsC&rdid=book-yodWha1LmPsC&rdot=1&source=gbs_api",
"offers": [
{
"finskyOfferType": 1,
"listPrice": {
"amountInMicros": 35040000,
"currencyCode": "TRY"
},
"retailPrice": {
"amountInMicros": 35040000,
"currencyCode": "TRY"
}
}
]
},
"accessInfo": {
"country": "TR",
"viewability": "PARTIAL",
"embeddable": true,
"publicDomain": false,
"textToSpeechPermission": "ALLOWED",
"epub": {
"isAvailable": true
},
"pdf": {
"isAvailable": true
},
"webReaderLink": "http://play.google.com/books/reader?id=yodWha1LmPsC&hl=&printsec=frontcover&source=gbs_api",
"accessViewStatus": "SAMPLE",
"quoteSharingAllowed": false
},
"searchInfo": {
"textSnippet": "<b>Momo</b> the Monkey Adventure Series <b>Momo</b> the Monkey Arrives <b>Momo</b> Makes a <br>\\nMess <b>Momo</b> Comes to the Rescue <b>Momo</b> Moves to the Orphanage Look out for <br>\\nthese stories about <b>Momo</b> and his friends. More coming soon! Master Publishing<br>\\n ..."
}
}
]
}
"""
struct Base : Codable {
let kind : String
let totalItems : Int
let items : [Items]
}
struct Items : Codable {
let kind : String
let id : String
let etag : String
let selfLink : String
let volumeInfo : VolumeInfo
let saleInfo : SaleInfo?
let accessInfo : AccessInfo
let searchInfo : SearchInfo?
}
struct SaleInfo : Codable {
let country : String
let saleability : String
let isEbook : Bool
let listPrice : ListPrice?
let retailPrice : RetailPrice?
let buyLink : String?
let offers : [Offers]?
}
struct Offers : Codable {
let finskyOfferType : Int
let listPrice : ListPrice
let retailPrice : RetailPrice
}
struct ListPrice : Codable {
let amountInMicros : Int?
let currencyCode : String
}
struct ReadingModes : Codable {
let text : Bool
let image : Bool
}
struct Epub : Codable {
let isAvailable : Bool
}
struct Pdf : Codable {
let isAvailable : Bool
}
struct RetailPrice : Codable {
let amountInMicros : Int?
let currencyCode : String
}
struct SearchInfo : Codable {
let textSnippet : String
}
struct ImageLinks : Codable {
let smallThumbnail : String
let thumbnail : String
}
struct IndustryIdentifiers : Codable {
let type : String
let identifier : String
}
struct PanelizationSummary : Codable {
let containsEpubBubbles : Bool
let containsImageBubbles : Bool
}
struct VolumeInfo : Codable {
let title : String
let authors : [String]
let publisher : String?
let publishedDate : String
let description : String?
let industryIdentifiers : [IndustryIdentifiers]
let readingModes : ReadingModes
let pageCount : Int
let printType : String
let categories : [String]
let maturityRating : String
let allowAnonLogging : Bool
let contentVersion : String
let panelizationSummary : PanelizationSummary?
let imageLinks : ImageLinks
let language : String
let previewLink : String
let infoLink : String
let canonicalVolumeLink : String
}
struct AccessInfo : Codable {
let country : String
let viewability : String
let embeddable : Bool
let publicDomain : Bool
let textToSpeechPermission : String
let epub : Epub
let pdf : Pdf
let webReaderLink : String
let accessViewStatus : String
let quoteSharingAllowed : Bool
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let data1 = json1.data(using: .utf8)!
do {
let result = try JSONDecoder().decode(Base.self, from: data1)
print("success1")
print(result)
} catch {
print(error)
}
let data2 = json2.data(using: .utf8)!
do {
let result = try JSONDecoder().decode(Base.self, from: data2)
print("success2")
print(result)
} catch {
print(error)
}
}
}

How to create Struct for more then one json table in swift xcode

I am writing an IOS Application that need to read a JSON FIle.
I understood the best way to do that is to write a struct for that json file and parse the json into that struct to be able to use freely.
I have a Json file that is saved locally in one of the folders
{
"colors": [
{
"color": "black",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,255,255,1],
"hex": "#000"
}
},
{
"color": "white",
"category": "value",
"code": {
"rgba": [0,0,0,1],
"hex": "#FFF"
}
},
{
"color": "red",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,0,0,1],
"hex": "#FF0"
}
},
{
"color": "blue",
"category": "hue",
"type": "primary",
"code": {
"rgba": [0,0,255,1],
"hex": "#00F"
}
},
{
"color": "yellow",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,255,0,1],
"hex": "#FF0"
}
},
{
"color": "green",
"category": "hue",
"type": "secondary",
"code": {
"rgba": [0,255,0,1],
"hex": "#0F0"
}
},
],
"people": [
{
"first_name": "john",
"is_valid": true,
"friends_list": {
"friend_names": ["black", "hub", "good"],
"age": 13
}
},
{
"first_name": "michal",
"is_valid": true,
"friends_list": {
"friend_names": ["jessy", "lyn", "good"],
"age": 19
}
},
{
"first_name": "sandy",
"is_valid": false,
"friends_list": {
"friend_names": ["brown", "hub", "good"],
"age": 15
}
},
]
}
i created a struct for each one of the two tables:
import Foundation
struct Color {
var color: String
var category: String
var type: String
var code: [JsonCodeStruct]
}
struct Poeople {
var firsName: String
var is_valid: Bool
var friendsNames: [JsonFriendNames]
}
struct JsonFriendNames {
var friendNames: [String]
var age: String
}
struct JsonCodeStruct {
var rgba: [Double]
var hex: String
}
and I want to open the local json file
and assign it the structs that I gave and then read them easily in the code.
can you suggest me a way on how to do that?
First of all you need an umbrella struct to decode the colors and people keys
struct Root: Decodable {
let colors: [Color]
let people : [Person]
}
The types in your structs are partially wrong. The Color related structs are
struct Color: Decodable {
let color: String
let category: String
let type: String?
let code : ColorCode
}
struct ColorCode: Decodable {
let rgba : [UInt8]
let hex : String
}
and the Person related structs are
struct Person: Decodable {
let firstName : String
let isValid : Bool
let friendsList : Friends
}
struct Friends: Decodable {
let friendNames : [String]
let age : Int
}
Assuming you read the file with
let data = try Data(contentsOf: URL(fileURLWithPath:"/...."))
you can decode the JSON into the given structs with
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let result = try decoder.decode(Root.self, from: data)
print(result)
} catch { print(error) }

Serde returns a SyntaxError "expected value" when deserializing nested JSON structs

I'm trying to deserialize a Spotify metadata JSON from the web API (specifications). I'm using hyper to retrieve the JSON from the server and serde to turn the JSON into something I can actually use within Rust. The JSON is retrieved from the server just fine, but when I try to turn the JSON into an object that can be used Rust panicks and throws an error:
thread '<main>' panicked at 'called 'Result::unwrap()' on an 'Err' value: SyntaxError("expected value", 11, 21)', ../src/libcore/result.rs:746
This is not helpful in the least way, because it doesn't indicate where things go wrong at all. When searching the web I stumbled upon a serde issue, which leads me to think that the problem is related to the nested structure of the JSON.
Can anyone see where things go wrong? Fixing the error would be the best solution for me, but if another crate turns out to be a better solution I'd like to hear that too. I've already tried rustc-serialize, but that crate can't handle the 'type' variables within the json.
The code I use is:
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
#![feature(custom_attribute)]
extern crate hyper;
extern crate serde;
extern crate serde_json;
use std::io::Read;
use hyper::Client;
use hyper::header::Connection;
#[derive(Serialize, Deserialize)]
struct Track_Full {
album: Album_Simp,
artists: Vec<Artist_Simp>,
available_markets: Vec<String>,
disc_number: u8,
duration_ms: u32,
explicit: bool,
external_ids: External_IDs,
external_urls: External_URLs,
href: String,
id: String,
name: String,
popularity: u8,
preview_url: String,
track_number: u8,
#[serde(rename="type")]
_type: String,
uri: String
}
#[derive(Serialize, Deserialize)]
struct Album_Simp {
album_type: String,
available_markets: Vec<String>,
external_urls: External_URLs,
href: String,
id: String,
images: Vec<Image>,
name: String,
#[serde(rename="type")]
_type: String,
uri: String
}
#[derive(Serialize, Deserialize)]
struct Artist_Simp {
external_urls: External_URLs,
href: String,
id: String,
name: String,
#[serde(rename="type")]
_type: String,
uri: String
}
#[derive(Serialize, Deserialize)]
struct External_IDs {
isrc: String
}
#[derive(Serialize, Deserialize)]
struct External_URLs {
spotify: String
}
#[derive(Serialize, Deserialize)]
struct Image {
height: u8,
url: String,
width: u8
}
fn main() {
// Create a client.
let mut client = Client::new();
// Creating an outgoing request.
let mut res = client.get("https://api.spotify.com/v1/tracks/0eGsygTp906u18L0Oimnem")
// set a header
.header(Connection::close())
// let 'er go!
.send().unwrap();
// Read the Response.
let mut body = String::new();
res.read_to_string(&mut body).unwrap();
println!("{}", body);
let deserialized: Track_Full = serde_json::from_str(&body).unwrap();
}
The JSON:
{
"album" : {
"album_type" : "album",
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GR", "GT", "HK", "HN", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "RO", "SE", "SG", "SI", "SK", "SV", "TR", "TW", "UY" ],
"external_urls" : {
"spotify" : "https://open.spotify.com/album/6TJmQnO44YE5BtTxH8pop1"
},
"href" : "https://api.spotify.com/v1/albums/6TJmQnO44YE5BtTxH8pop1",
"id" : "6TJmQnO44YE5BtTxH8pop1",
"images" : [ {
"height" : 640,
"url" : "https://i.scdn.co/image/8e13218039f81b000553e25522a7f0d7a0600f2e",
"width" : 629
}, {
"height" : 300,
"url" : "https://i.scdn.co/image/8c1e066b5d1045038437d92815d49987f519e44f",
"width" : 295
}, {
"height" : 64,
"url" : "https://i.scdn.co/image/d49268a8fc0768084f4750cf1647709e89a27172",
"width" : 63
} ],
"name" : "Hot Fuss",
"type" : "album",
"uri" : "spotify:album:6TJmQnO44YE5BtTxH8pop1"
},
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/0C0XlULifJtAgn6ZNCW2eu"
},
"href" : "https://api.spotify.com/v1/artists/0C0XlULifJtAgn6ZNCW2eu",
"id" : "0C0XlULifJtAgn6ZNCW2eu",
"name" : "The Killers",
"type" : "artist",
"uri" : "spotify:artist:0C0XlULifJtAgn6ZNCW2eu"
} ],
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GR", "GT", "HK", "HN", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "RO", "SE", "SG", "SI", "SK", "SV", "TR", "TW", "UY" ],
"disc_number" : 1,
"duration_ms" : 222075,
"explicit" : false,
"external_ids" : {
"isrc" : "USIR20400274"
},
"external_urls" : {
"spotify" : "https://open.spotify.com/track/0eGsygTp906u18L0Oimnem"
},
"href" : "https://api.spotify.com/v1/tracks/0eGsygTp906u18L0Oimnem",
"id" : "0eGsygTp906u18L0Oimnem",
"name" : "Mr. Brightside",
"popularity" : 74,
"preview_url" : "https://p.scdn.co/mp3-preview/934da7155ec15deb326635d69d050543ecbee2b4",
"track_number" : 2,
"type" : "track",
"uri" : "spotify:track:0eGsygTp906u18L0Oimnem"
}
You've attempted to parse some JSON and it failed. When you called unwrap on the Result, the program panicked because of this failure:
SyntaxError("expected value", 11, 21)
The documentation for SyntaxError says the numbers are the line and column of the error. Line 11, column 21 is:
"height" : 640,
^
Looking at your structure, you have declared the height to be a u8, an 8-bit unsigned number. This has the allowed values of 0-255. 640 does not fit into that. Increasing the value to a u32 allows the JSON to be parsed.
Additionally, Rust style is to use CamelCase identifiers without consecutive capital letters for structs. External_URLs -> ExternalUrls. The compiler will actually warn you about this.

nested dictionaries converted to json swift

I Have a Dictionary of Dictionaries that I need to convert to Json.
[
Dict1:1,
test: A Value,
NestedDict1:[
city: A City Name,
address: An Address,
NestedDict2: [
1: 1,
39: 2
],
favorite1: 2,
favorite3: Chocolate
]
]
When I use the
NSJSONSerialization.dataWithJSONObject(myJsonDict, options: NSJSONWritingOptions.PrettyPrinted, error: nil)
it only encodes the outer most dictionary. So my output looks something like this:
{
"Dict1":"1",
"test": "A Value",
"NestedDict1":"[
city: A City Name,
address: An Address,
NestedDict2: [
1: 1,
39: 2
],
favorite1: 2,
favorite3: Chocolate
]"
}
How do I JSON the inner dictionaries as well?
Swift 3
let myJsonDict : [String: Any] = [
"Dict1": "1",
"test": "A Value",
"NestedDict1":[
"city": "A City Name",
"address": "An Address",
"NestedDict2": [
"1": "1",
"39": "2"
],
"favorite1": "2",
"favorite3": "Chocolate"
]
]
let jsonObject = try? JSONSerialization.data(withJSONObject: myJsonDict, options: [])
if let jsonString = String(data: jsonObject!, encoding: .utf8) {
print(jsonString)
}
Output
{"test":"A Value","Dict1":"1","NestedDict1":{"favorite1":2,"city":"A
City
Name","NestedDict2":{"1":"1","39":"2"},"favorite3":"Chocolate","address":"An
Address"}}
I think the problem is more with your representation of the data.
If you can get away with changing all the keys to Strings, then it can be a dictionary, since Strings conform to Hashable. Otherwise, it would be defined as Any, and can't really be a Dictionary key.
Doing this allows the following:
let myJsonDict : [String:AnyObject] = [
"value": 1123,
"test": "some string",
"NestedDict1": [
"city": "A City Name",
"address": "An Address",
"NestedDict2": [
"1": 1,
"39": 2
],
"favorite1": 2,
"favorite3": "Chocolate"
]
]
var jsonObject = NSJSONSerialization.dataWithJSONObject(myJsonDict, options: NSJSONWritingOptions.PrettyPrinted, error: nil)
println(NSString(data: jsonObject!, encoding: NSUTF8StringEncoding)!)
which gives the following output:
{
"test" : "some string",
"NestedDict1" : {
"city" : "A City Name",
"address" : "An Address",
"favorite3" : "Chocolate",
"NestedDict2" : {
"1" : 1,
"39" : 2
},
"favorite1" : 2
},
"value" : 1123
}