Why do I can't access all the properties inside of "data" struct by doing this way? Otherwise, I don't know how to parse all the data inside of "Data" array.
This is how I'm trying to serialize:
import Foundation
import Alamofire
struct Description: Decodable {
let _data: [data]
}
struct data: Decodable {
let id:Int?
let descricao:String?
let urlImagem:String?
}
func callApi() {
guard let _url = URL(string: "https://alodjinha.herokuapp.com/categoria")else{return}
Alamofire.request(_url).responseJSON { (response) in
guard let _data = response.data else{return}
//let dataString = String(data: _data, encoding: .utf8)
do{
let dataParsed = try JSONDecoder().decode([Description].self, from: _data)
print(dataParsed.id)
}catch{
print("Error serialization")}
}
}
I'm getting the error:
Error serialization: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
The JSON model that I'm following:
{
"data":[
{
"id":1,
"descricao":"Games",
"urlImagem":"http://39ahd9aq5l9101brf3b8dq58.wpengine.netdna-cdn.com/wp-content/uploads/2013/06/3D-Gaming.png"
},
{
"id":2,
"descricao":"Livros",
"urlImagem":"http://4.bp.blogspot.com/-6Bta1H9d22g/UJAIJbqcHhI/AAAAAAAAKi4/hvgjWrlFc64/s1600/resenha-missiologia.png"
},
{
"id":3,
"descricao":"Celulares",
"urlImagem":"http://pt.seaicons.com/wp-content/uploads/2015/11/Mobile-Smartphone-icon.png"
},
{
"id":4,
"descricao":"Informática",
"urlImagem":"http://portal.ifrn.edu.br/campus/ceara-mirim/noticias/ifrn-oferece-curso-de-informatica-basica-para-pais-dos-estudantes/image_preview"
},
{
"id":5,
"descricao":"Eletrodoméstico",
"urlImagem":"http://classificados.folharegiao.com.br/files/classificados_categoria/photo/8/sm_4d5ed3beb0f31b61cb9a01e46ecd0cf9.png"
},
{
"id":6,
"descricao":"TVs",
"urlImagem":"http://i.utdstc.com/icons/256/terrarium-tv-android.png"
},
{
"id":7,
"descricao":"Filmes e Séries",
"urlImagem":"https://pbs.twimg.com/profile_images/801033586438733824/91Y_N91t_reasonably_small.jpg"
},
{
"id":8,
"descricao":"Móveis e Decorações",
"urlImagem":"https://image.flaticon.com/icons/png/128/148/148188.png"
},
{
"id":9,
"descricao":"Moda, Beleza e Perfumaria",
"urlImagem":"http://icon-icons.com/icons2/196/PNG/128/fashion_23852.png"
},
{
"id":10,
"descricao":"Papelaria",
"urlImagem":"http://esen.pt/in/images/stories/skills_256.png"
}
]
}
First, the error indicates that you only have one Description in the JSON but your code is attempting to get an array.
You need to change:
let dataParsed = try JSONDecoder().decode([Description].self, from: _data)
to:
let dataParsed = try JSONDecoder().decode(Description.self, from: _data)
Now you need to iterate the array of data.
This means you need code similar to:
for aData in dataParsed._data {
print(aData.id)
}
FYI - class, struct, and enum names should start with uppercase letters. Functions, variables, and case names should start with lowercase letters.
Also avoid using _ in variable names.
Related
I am receiving some JSON which looks like the below :
{
"template": "search",
"item": "2",
"contents": [
{
"title": "title 1",
"subtitle": "subtitle 1",
"imageurl": "/data/dzzxw0177014_325qv.jpg?size=small",
"fullscreenimageurl": "/data/xw0177014_325qv.jpg?size=large",
"id": "0177014",
"detaillink": "/apps/v2/details/programme/177014",
"duration": "PT2H46M"
},
{
"title": "title2",
"subtitle": "subtitle 2",
"imageurl": "/data_p//11436/origin_dzdzdzdzw0046394_43fu1.jpg?size=small",
"fullscreenimageurl": "/data/11456/w0046394_43fu1.jpg?size=large",
"id": "0046394",
"detaillink": "/apps/v2/details/programme/MYW0046394",
"duration": "PT1H40M46S"
}
]
}
and I have a corresponding model:
import Foundation
// MARK: - Welcome
struct Welcome {
let template, item: String
let contents: [Content]
}
// MARK: - Content
struct Content {
let title, subtitle, imageurl, fullscreenimageurl: String
let id, detaillink, duration: String
}
I have an API manager :
import Foundation
import Combine
class APIManager {
static let shared = APIManager()
let baseURL = "https:// ....."
func fetchShows(with query: String) -> AnyPublisher<[Content], Error > {
Future<Any, Error> { promise in
self.loadJson(withQuery: query) { (result) in
promise(.success(result))
}
}
.tryMap {
try JSONSerialization.data(withJSONObject: $0, options: .prettyPrinted)
}
.decode(type: [Content].self, decoder: jsonDecoder)
.eraseToAnyPublisher()
}
var jsonDecoder: JSONDecoder {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}
func loadJson(withQuery query: String,
completion: #escaping (Result<Data, Error>) -> Void) {
let UrlString = baseURL + (query.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "")
if let url = URL(string: UrlString) {
print (UrlString)
let urlSession = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
if let error = error {
completion(.failure(error))
}
if let data = data {
completion(.success(data))
}
}
urlSession.resume()
}
}
}
At the moment I have a crash with the error "Invalid top-level type in JSON write", I assume this is because the JSON that I am trying to decode isn't an array of Content. It's a Welcome Struct which contains an array of Content.
At what point can I say that I am only interested in the Contents "Array" and decode it ? Should this be defined in the model some how ?
Thanks
You can use .map operator to transform your data objects.
.decode(type: Welcome.self, decoder: jsonDecoder) // <- here
.map { $0.contents } // <- here
.eraseToAnyPublisher()
In addition, you have to confirm your data objects to Decodable.
Adding Decodable keyword is enough, Since all the files types are Decodable here,
struct Welcome: Decodable { //<- Here
let template, item: String
let contents: [Content]
}
struct Content: Decodable { //<- Here
let title, subtitle, imageurl, fullscreenimageurl: String
let id, detaillink, duration: String
}
in my very basic code I need get data from JSON. But i get only one value.
i have this input JSON:
{
"features":
[
{
"geometry":
{
"coordinates": [
14.49961,
50.03353
],
"type": "Point"
}
},
{
"geometry":
{
"coordinates": [
14.4213,
50.00144
],
"type": "Point"
}
}
],
"type": "FeatureCollection"
}
and this code in SwiftUI, where i need get all data throw JSON. Value of field "type" work, but rest of field i can't get - what i do wrong?
import Foundation
import SwiftUI
import MapKit
import Combine
class fetchResults{
func getData(completion: #escaping (RequestA) -> ()){
let url = URL(string: "https://api.golemio.cz/v2/vehiclepositions?limit=2")!
var request = URLRequest(url: url)
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("XXXXXXXXXXXXXXXX", forHTTPHeaderField: "x-access-token")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
let result = try! JSONDecoder().decode(RequestA.self, from: data!)
if let response = response {
if let data = data, let body = String(data: data, encoding: .utf8) {
DispatchQueue.main.async {
completion(result)
}
}
} else {
print(error ?? "Unknown error")
}
}
task.resume()
}
}
struct RequestA: Decodable {
var features: [Features]!
var type: String
}
struct Features: Decodable {
var geometry: Geometry
}
struct Geometry: Decodable {
var coordinates: [Double]
var type: String
}
struct ContentView: View {
#State var res = RequestA()
var body: some View {
Text(res.type ?? "N/A") // <<< "res.type" work but "res.features.geometry.coordinates[0] not
.onAppear()
{
fetchResults().getData
{(res) in
self.res = res
}
}
}
}
I will be very glad, if somebody help me a show me, what i do wrong.
res.features.geometry.coordinates
res is RequestA and a "RequestA" has a property features which is of type [Features] - an array of "Features"
Array of features doesn't have a property "geometry" so you can't write an expression like res.features.geometry
Did you mean for RequestA "features" property to be of type Features not [Features]? Or did you mean to do something for each one of the features in the array, or perhaps the first one etc?
If you're interested in parsing GeoJSON, then see Swift GeoJson Parse
If someone will solve a similar problem, I solved it like this inside body View:
print(res.features![0].geometry.coordinates[0])
Karel
I am trying to decode a json data, but it throws an error:
[] nw_protocol_get_quic_image_block_invoke dlopen libquic failed responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))))
I think, I did it in a correct way, I checked for the type mismatch also, looks correct, but could not comprehend why am i still getting the error. .
here is how my json data response looks like:
[
{
"id": 1,
"name": "BlackJet",
"type": "Silk",
"quantity": "100"
},
[
{
"id": 2,
"name": "Toto",
"type": "Leather",
"quantity": "10"
},
...,
]
here is my struct data:
import Foundation
struct Response<T: Codable>: Codable {
var data: [T]?
}
struct MyData: Codable {
var id: Int
var name: String
var type: String
var quantity: Int
}
and my serverCommunicator func:
static func getData() -> Promise<Response<MyData>> {
let decoder = JSONDecoder()
return Promise { seal in
AF.request(API.getData, method: .get, parameters: .none).responseDecodable(of: Response<MyData>.self, decoder: decoder) { response in
switch response.result {
case .success(let val):
return seal.fulfill(val)
case .failure(let err):
return seal.reject(err)
}
}
}
}
and my apiCall func inside VC didload:
func getData() {
ServerCommunicator.getData().done { response -> Void in
guard response.data != nil, let data = response.data else {
print("Data could not be obtained.. .")
return
}
self.data = data
}.catch { (err) in
print(err)
}
}
NOTE: No api headers or parametrs exist in my api//
First you need to change the type of quantity from Int to String as per the response,
struct MyData: Codable {
var id: Int
var name: String
var type: String
var quantity: String
}
Then you will need to change the signature of getData method as below,
static func getData() -> Promise<[MyData]> {
let decoder = JSONDecoder()
return Promise { seal in
AF.request(API.getData, method: .get, parameters: .none).responseDecodable(of: [MyData].self, decoder: decoder) { response in
switch response.result {
case .success(let val):
return seal.fulfill(val)
case .failure(let err):
return seal.reject(err)
}
}
}
}
My json looks like this:
[
{
"name": "sensei",
"owner": {
"login": "linkedin",
},
"description": "distributed realtime searchable database",
"fork": false,
},
{
"name": "linkedin-utils",
"owner": {
"login": "linkedin",
},
"description": "Base utilities shared by all linkedin open source projects",
"fork": false,
}
]
The structs I built are the following:
struct LinkedinData: Codable {
var name: String
var description: String
var owner: OwnerLogin
var fork: Bool
}
struct OwnerLogin: Codable {
var login: String
}
My code for parsing is this one:
import UIKit
class ViewController: UIViewController {
var linkedinData = [LinkedinData]()
override func viewDidLoad() {
super.viewDidLoad()
let urString : String = "https://api.github.com/orgs/linkedin/repos"
if let url = URL(string: urString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return //exit out of function
}
parseJSON(json: data!)
}
task.resume()
}
func parseJSON(json: Data) {
let decoder = JSONDecoder()
if let decodedData = try? decoder.decode(LinkedinData.self, from: json) {
linkedinData = [decodedData]
}
}
}
I tried for hours bút it seems impossible to parse the json and retreive the data I am looking for (name, description, owner.login and fork) in a collection type. Could you please help?
You should decode an array of LinkedinData, instead of just one, because your JSON has an array as its root:
[ <------- this "[" indicates an array
{
"name": "sensei",
"owner": {
"login": "linkedin",
},
Therefore, you should write:
if let decodedData = try? decoder.decode([LinkedinData].self, from: json) {
linkedinData = decodedData
}
if let decodedData = try? decoder.decode(LinkedinData.self, from: json) {
linkedinData = [decodedData]
}
replace this with
if let decodedData = try? decoder.decode([LinkedinData].self, from: json) {
linkedinData = decodedData
}
as your topmost object in JSON is an Array.
Since yesterday when I updated xCode I'm receiving error in the console and I'm not getting my data from the API. I'm getting this error:
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
What I don't understand is that I never had any issues before and now I get this error out of nowhere, and I also don't know if the problem is server sided or in my swift code...
Here if how I make the request:
// -- 1 -- Create an URL
if let url = URL(string: urlString){
// -- 2 -- Create a URLSession
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil{
print(error!)
return
}
if let safeData = data {
self.parseJSON(EventDatas: safeData)
}
}
task.resume()
}
}
func parseJSON(EventDatas: Data){
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(LuxCategoryData.self, from: EventDatas)
var test: [Int] = []
for object in decodedData.category {
let category: CategoryData = CategoryData()
category.idCategory = object.idCategory
category.dtTitle = object.dtTitle
dropDown.optionArray.append(category.dtTitle)
test.append(Int(category.idCategory)!)
self.categoryData.append(category)
}
dropDown.optionIds = test
}catch{
print(error)
}
}
Here is the decodable struct I use to parse the JSON:
struct LuxCategoryData : Decodable {
let category: [Category]
}
struct Category: Decodable {
let idCategory: String;
let dtTitle: String;
}
This is how my JSON look like when I make a request in the browser:
{
category: [
{
idCategory: "1",
dtTitle: "Cinema"
},
{
idCategory: "2",
dtTitle: "Bar"
},
{
idCategory: "5",
dtTitle: "Danse"
},
{
idCategory: "6",
dtTitle: "Nightlife"
},
{
idCategory: "10",
dtTitle: "Music"
}
]
}
The JSON you provided doesn't contain " " around the keys. That's why it is giving invalid JSON error.
Try with the below JSON format,
{"category":[{"idCategory":"1","dtTitle":"Cinema"},{"idCategory":"2","dtTitle":"Bar"},{"idCategory":"5","dtTitle":"Danse"},{"idCategory":"6","dtTitle":"Nightlife"},{"idCategory":"10","dtTitle":"Music"}]}
Error: Parse error on line 1:
{ category: [{ idCa
--^
Expecting 'STRING', '}', got 'undefined'
https://jsonlint.com/