I'm using SwiftSoup in combination with Codable to get the correct element(s) and parse the data. However, I receive this error:
JSON decode failed: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.})))
I've looked online, ensure my data matches correctly, even change the expected types from Int to String but got an error stating an Int was expected.
This is the data I am receiving and can print in the console:
data -
{"test":0,"ppace":85,"pshooting":92,"ppassing":91,"pdribbling":95,"pdefending":38,"pphysical":65,"acceleration":91,"sprintspeed":80,"agility":91,"balance":95,"reactions":94,"ballcontrol":96,"dribbling":96,"positioning":93,"finishing":95,"shotpower":86,"longshotsaccuracy":94,"volleys":88,"penalties":75,"interceptions":40,"headingaccuracy":70,"marking":32,"standingtackle":35,"slidingtackle":24,"vision":95,"crossing":85,"freekickaccuracy":94,"shortpassing":91,"longpassing":91,"curve":93,"jumping":68,"stamina":72,"strength":69,"aggression":44,"composure":96}
This is my function:
func parseData() {
do {
let html = try String(contentsOf: url!, encoding: String.Encoding.ascii)
let doc: Document = try! SwiftSoup.parse(html)
let elements = try doc.getAllElements()
for element in elements {
switch element.id() {
case "player_stats_json":
let content = try! element.getElementsContainingText("ppace").text()
print(content.utf8)
let jsonData = content.data(using: .utf8)
do {
self.player = try JSONDecoder().decode(PlayerModel.self, from: Data(jsonData!))
} catch let jsonError as NSError {
print("JSON decode failed: \(jsonError)")
print("data - \(content)")
}
default:
break
}
}
} catch Exception.Error(type: let type, Message: let message) {
print(type)
print(message)
} catch {
print("")
}
}
And this is my model file:
struct PlayerModel: Codable {
var test: Int
var ppace: Int
var pshooting: Int
var ppassing: Int
var pdribbling: Int
var pdefending: Int
var pphysical: Int
var acceleration: Int
var sprintspeed: Int
var agility: Int
var balance: Int
var reactions: Int
var ballcontrol: Int
var dribbling: Int
var positioning: Int
var finishing: Int
var shotpower: Int
var longshotsaccuracy: Int
var volleys: Int
var penalties: Int
var interceptions: Int
var headingaccuracy: Int
var marking: Int
var standingtackle: Int
var slidingtackle: Int
var vision: Int
var crossing: Int
var freekickaccuracy: Int
var shortpassing: Int
var longpassing: Int
var curve: Int
var jumping: Int
var stamina: Int
var strength: Int
var aggression: Int
var composure: Int
}
I've double and tripled checked the variable names match up with each key. I've pasted the outputted data in the console in an Online JSON validator, and no errors appear. I'm genuinley left scratching my head right now, because I can't understand where I've gone wrong.
I still have no idea whats wrong with your parsing. I've managed to parse your html but I have no idea why when trying to use Int instead of Uint8 it kept failing to return the right values. This is what worked for me:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
struct PlayerModel: Codable {
let test: UInt8
let ppace: UInt8
let pshooting: UInt8
let ppassing: UInt8
let pdribbling: UInt8
let pdefending: UInt8
let pphysical: UInt8
let acceleration: UInt8
let sprintspeed: UInt8
let agility: UInt8
let balance: UInt8
let reactions: UInt8
let ballcontrol: UInt8
let dribbling: UInt8
let positioning: UInt8
let finishing: UInt8
let shotpower: UInt8
let longshotsaccuracy: UInt8
let volleys: UInt8
let penalties: UInt8
let interceptions: UInt8
let headingaccuracy: UInt8
let marking: UInt8
let standingtackle: UInt8
let slidingtackle: UInt8
let vision: UInt8
let crossing: UInt8
let freekickaccuracy: UInt8
let shortpassing: UInt8
let longpassing: UInt8
let curve: UInt8
let jumping: UInt8
let stamina: UInt8
let strength: UInt8
let aggression: UInt8
let composure: UInt8
}
let url = URL(string:"https://www.futbin.com/21/player/541/lionel-messi")!
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, let html = String(data: data, encoding: .utf8) else { return }
if let start = html.range(of: #"<div style="display: none;" id="player_stats_json">"#)?.upperBound,
let end = html[start...].range(of: #"</div>"#)?.lowerBound {
let json = html[start..<end]
do {
let player = try JSONDecoder().decode(PlayerModel.self, from: Data(json.utf8))
print(player)
} catch {
print(error)
}
}
}.resume()
This will print
PlayerModel(test: 0, ppace: 85, pshooting: 92, ppassing: 91,
pdribbling: 95, pdefending: 38, pphysical: 65, acceleration: 91,
sprintspeed: 80, agility: 91, balance: 95, reactions: 94, ballcontrol:
96, dribbling: 96, positioning: 93, finishing: 95, shotpower: 86,
longshotsaccuracy: 94, volleys: 88, penalties: 75, interceptions: 40,
headingaccuracy: 70, marking: 32, standingtackle: 35, slidingtackle:
24, vision: 95, crossing: 85, freekickaccuracy: 94, shortpassing: 91,
longpassing: 91, curve: 93, jumping: 68, stamina: 72, strength: 69,
aggression: 44, composure: 96)
Related
Im trying to make a request on Swift UI in a data file, but nothing is happening on and I seem to not be getting back any data. Im not sure where the issue is, keep in mind I just started swiftUI 2days ago. Any help would be greatly appreciated.
Data Im trying to retrieve.
[
{
"id": 1119,
"make": "Audi",
"model": "100",
"generation": "100 Avant (4A,C4)",
"engine_modification": "2.8 V6 E (174 Hp) quattro Automatic",
"year": 1991,
"powertrain_architecture": "Internal Combustion engine",
"body_type": "Station wagon (estate)",
"number_of_seats": 5,
"number_of_doors": 5,
"urban_fuel_consumption": 13.5,
"extra_urban_fuel_consumption": 8,
"combined_fuel_consumption": 10,
"fuel_type": "Petrol (Gasoline)",
"acceleration": 9.5,
"top_speed": 207,
"power": 174,
"torque": 250,
"engine_location": "Front, Longitudinal",
"engine_displacement": 2771,
"number_of_cylinders": 6,
"position_of_cylinders": "V-engine",
"number_of_valves_per_cylinder": 2,
"fuel_system": "Multi-point indirect injection",
"engine_aspiration": "Naturally aspirated engine",
"kerb_weight": 1550,
"fuel_tank_capacity": 80,
"drive_wheel": "All wheel drive (4x4)",
"number_of_gears": 4,
"front_brakes": "Ventilated discs",
"rear_brakes": "Disc"
},
{
"id": 1120,
"make": "Audi",
"model": "100",
"generation": "100 Avant (4A,C4)",
"engine_modification": "2.8 V6 E (174 Hp) quattro",
"year": 1991,
"powertrain_architecture": "Internal Combustion engine",
"body_type": "Station wagon (estate)",
"number_of_seats": 5,
"number_of_doors": 5,
"urban_fuel_consumption": 13.5,
"extra_urban_fuel_consumption": 8,
"combined_fuel_consumption": 10,
"fuel_type": "Petrol (Gasoline)",
"acceleration": null,
"top_speed": null,
"power": 174,
"torque": 250,
"engine_location": "Front, Longitudinal",
"engine_displacement": 2771,
"number_of_cylinders": 6,
"position_of_cylinders": "V-engine",
"number_of_valves_per_cylinder": 2,
"fuel_system": "Multi-point indirect injection",
"engine_aspiration": "Naturally aspirated engine",
"kerb_weight": 1550,
"fuel_tank_capacity": 80,
"drive_wheel": "All wheel drive (4x4)",
"number_of_gears": 5,
"front_brakes": "Ventilated discs",
"rear_brakes": "Disc"
},
Code for request
import SwiftUI
import Foundation
struct astonMartin: Decodable, Identifiable{
let id: Int
var make: String
var model: String
var generation: String
var engine_modification: String
var year: Int
var powertrain_architecture: String
var body_type: String
var number_of_seats: Int
var number_of_doors: Int
var urban_fuel_consumption: Int
var extra_urban_fuel_consumption: Int
var combined_fuel_consumption: Int
var fuel_type: String
var acceleration: Int
var top_speed: Int
var power: Int
var torque: Int
var engine_location: String
var engine_displacement: Int
var number_of_cylinders: Int
var position_of_cylinders: String
var number_of_valves_per_cylinder: Int
var fuel_system: String
var engine_aspiration: String
var kerb_weight: Int
var fuel_tank_capacity: Int
var drive_wheel: String
var number_of_gears: Int
var front_brakes: String
var rear_brakes: String
}
class secondaryCall: ObservableObject {
#Published var cars: [astonMartin] = []
func getCars() {
guard let url = URL(string: "URL INSERTED HERE") else { fatalError("Missing URL") }
let urlRequest = URLRequest(url: url)
let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let error = error {
print("Request error: ", error)
return
}
guard let response = response as? HTTPURLResponse else { return }
if response.statusCode == 200 {
guard let data = data else { return }
DispatchQueue.main.async {
do {
let decodedCars = try JSONDecoder().decode([astonMartin].self, from: data)
self.cars = decodedCars
} catch let error {
print("Error decoding: ", error)
}
}
}
}
dataTask.resume()
}
}
content view
import SwiftUI
struct ContentView: View {
#State var isLinkActive = false
#EnvironmentObject var allCars: secondaryCall
var body: some View {
NavigationView{
ZStack{
Image("car")
.resizable()
.scaledToFill()
.ignoresSafeArea(.all)
.overlay(
Rectangle()
.opacity(0.6)
.ignoresSafeArea(.all)
)
NavigationLink(destination: HomeView()) {
Text("Click to view all cars")
.foregroundColor(.white)
}
.padding()
.background(.orange)
.clipShape(Capsule()
)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(secondaryCall())
}
}
Home view:
//
// HomeView.swift
// project
//
//
import SwiftUI
struct HomeView: View {
#EnvironmentObject var allCars: secondaryCall
var body: some View {
ScrollView{
Text("All cars for Aston Martin")
.bold()
.textCase(.uppercase)
.font(.headline)
ForEach(allCars.cars){car in
HStack(alignment: .top){
Text("\(car.make)")
}
}
}
.onAppear{
allCars.getCars()
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
.environmentObject(secondaryCall())
}
}
Project App
//
// projectApp.swift
// project
//
//
import SwiftUI
#main
struct projectApp: App {
var allCars = secondaryCall()
var body: some Scene {
WindowGroup {
SplashView()
.environmentObject(allCars)
}
}
}
Form your data, I see that the reason you not getting data is that because you construct some variables in struct astonMartin with wrong type compare to the JSON you given.
Given you some example
In Json: "urban_fuel_consumption": 13.5, which should be Double but in struct astonMartin you set is Int
In Json: "acceleration": null, which should be Double? because it is nil here but in struct astonMartin you set is Double
In Json: "top_speed": null, which should be Int? because it is nil here but in struct astonMartin you set is Int
And maybe more of that ...
You should recheck each type of variables in your struct
Code will change like follow
struct astonMartin: Decodable, Identifiable {
let id: Int
var make: String
var model: String
var generation: String
var engine_modification: String
var year: Int
var powertrain_architecture: String
var body_type: String
var number_of_seats: Int
var number_of_doors: Int
var urban_fuel_consumption: Double // was Int
var extra_urban_fuel_consumption: Int
var combined_fuel_consumption: Int
var fuel_type: String
var acceleration: Double? // was Int
var top_speed: Int? // was Int
var power: Int
var torque: Int
var engine_location: String
var engine_displacement: Int
var number_of_cylinders: Int
var position_of_cylinders: String
var number_of_valves_per_cylinder: Int
var fuel_system: String
var engine_aspiration: String
var kerb_weight: Int
var fuel_tank_capacity: Int
var drive_wheel: String
var number_of_gears: Int
var front_brakes: String
var rear_brakes: String
}
More over, when you parse json not right it prints error in debug mode because you currently catch error there. Make sure you notice that.
wonder if there is a simple way to get a simple flat json to a struct with a structure
json:
{
"date": "2022-02-24T00:00:00.000Z",
"personel": 800,
"plane": 7,
"drone": 6,
}
what I want is to get a structure like this:
struct Day: Codable, Identifiable {
var id = UUID()
var date : String
var dayData: [DayData]
struct DayData: Codable {
var personel: Int
var plane: Int
var drone: Int
}
}
I thought there should be some simple way to make it work
As specified in comments it would be better to create temporary DBObject and initialize your Day object with it.
Simple DBObject:
struct DBObject: Decodable {
let date: String?
let personel, plane, drone: Int?
}
Adding init() to your Day
struct Day: Codable, Identifiable {
var id = UUID()
var date : String
var dayData: [DayData]
init(from dbObject: DBObject) {
let dayData = DayData(personel: dbObject.personel ?? 0,
plane: dbObject.plane ?? 0,
drone: dbObject.drone ?? 0)
self.dayData = [dayData]
self.date = dbObject.date ?? ""
}
struct DayData: Codable {
var personel: Int
var plane: Int
var drone: Int
}
}
Decoding Day from Data response:
func returnDay(from dataResponse: Data) -> Day {
do {
let decoder = JSONDecoder()
let dbObject = try decoder.decode(DBObject.self,
from: dataResponse)
return Day(from: dbObject)
} catch {
fatalError("Cannot decode object")
}
}
{
"optionChain": {
"result": [
{
"underlyingSymbol": "AAPL",
"expirationDates": [
1606435200,
1607040000,
1607644800,
1608249600,
1608768000,
1609372800,
1610668800,
1613692800,
1616112000,
1618531200,
1623974400,
1626393600,
1631836800,
1642723200,
1655424000,
1663286400,
1674172800
],
"strikes": [
55,
60,
65,
70,
75
],
"hasMiniOptions": false,
How can I print out "underlyingSymbol" or any of those related fields in JSON? I understand printing out a single JSON field, but how do I get into the embedded ones?
So I made the Decodable as Suggested below:
struct Something: Decodable
{
let optionChain: OptionChain
let error: String
}
struct OptionChain: Decodable
{
let result: [ResultElement]
}
struct ResultElement: Decodable
{
let underlyingSymbol: String
let expirationDates: [Int]
let strikes: [Int]
let hasMiniOptions: Bool
let quote: [quoteElement]
let options: [optionsElement]
}
struct quoteElement: Decodable
{
let language: String
let region: String
let quoteType: String
let quoteSourceName: String
let triggerable: Bool
let currency: String
let firstTradeDateMilliseconds: Int
let priceHint: Int
let regularMarketChange: Int
let regularMarketChangePercent: Int
let regularMarketTime: Int
let regularMarketPrice: Int
let regularMarketDayHigh: Int
let regularMarketDayRange: String
let regularMarketDayLow: Int
let regularMarketVolume: Int
let regularMarketPreviousClose: Int
let bid: Int
let ask: Int
let bidSize: Int
let askSize: Int
let fullExchangeName: String
let financialCurrency: String
let regularMarketOpen: Int
let averageDailyVolume3Month: Int
let averageDailyVolume10Day: Int
let fiftyTwoWeekLowChange: Int
let fiftyTwoWeekLowChangePercent: Int
let fiftyTwoWeekRange: String
let fiftyTwoWeekHighChange: Int
let fiftyTwoWeekHighChangePercent: Int
let fiftyTwoWeekLow: Int
let fiftyTwoWeekHigh: Int
let dividendDate: Int
let earningsTimestamp: Int
let earningsTimestampStart: Int
let earningsTimestampEnd: Int
let trailingAnnualDividendRate: Int
let trailingPE: Int
let trailingAnnualDividendYield: Int
let epsTrailingTwelveMonths: Int
let epsForward: Int
let epsCurrentYear: Int
let priceEpsCurrentYear: Int
let sharesOutstanding: Int
let bookValue: Int
let fiftyDayAverage: Int
let fiftyDayAverageChange: Int
let fiftyDayAverageChangePercent: Int
let twoHundredDayAverage: Int
let twoHundredDayAverageChange: Int
let twoHundredDayAverageChangePercent: Int
let marketCap: Int
let forwardPE: Int
let priceToBook: Int
let sourceInterval: Int
let exchangeDataDelayedBy: Int
let tradeable: Bool
let exchange: String
let shortName: String
let longName: String
let marketState: String
let messageBoardId: String
let exchangeTimezoneName: String
let exchangeTimezoneShortName: String
let gmtOffSetMilliseconds: Int
let market: String
let esgPopulated: Bool
let displayName: String
let symbol: String
}
struct optionsElement: Decodable
{
let expirationDate: Int
let hasMiniOptions: Bool
let calls: [callPutElement]
let puts: [callPutElement]
}
struct callPutElement: Decodable
{
let contractSymbol: String
let strike: Int
let currency: String
let lastPrice: Int
let change: Int
let percentChange: Int
let volume: Int
let openInterest: Int
let bid: Int
let ask: Int
let contractSize: String
let expiration: Int
let lastTradeDate: Int
let impliedVolatility: Int
let inTheMoney: Bool
}
However now I am receiving an error:
JSONSerialization 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 1." UserInfo={NSDebugDescription=No string key for value in object around character 1.})))
I'd use the Codable apis:
struct Something: Decodable {
let optionChain: OptionChain
}
struct OptionChain: Decodable {
let result: [ResultElement]
}
struct ResultElement: Decodable {
let underlyingSymbol: String
let expirationDates: [Int]
let strikes: [Int]
let hasMiniOptions: Bool
}
let jsonString = "{...}"
let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let something = try decoder.decode(Something.self, from: jsonData)
print(something.optionChain.result.map { $0.underlyingSymbol })
func getKoreacountryChart() {
AF.request("https://api.corona-19.kr/korea/country/new/?", headers: headers).responseJSON { response in
let result = response.data
if result != nil {
let json = JSON(result!)
let seoul = json["seoul"]["totalCase"].intValue
let busan = json["busan"]["totalCase"].intValue
let daegu = json["daegu"]["totalCase"].intValue
let incheon = json["incheon"]["totalCase"].intValue
let gwangju = json["gwangju"]["totalCase"].intValue
let daejeon = json["daejeon"]["totalCase"].intValue
let ulsan = json["ulsan"]["totalCase"].intValue
let sejong = json["sejong"]["totalCase"].intValue
let gyeonggi = json["gyeonggi"]["totalCase"].intValue
let gangwon = json["gangwon"]["totalCase"].intValue
let chungbuk = json["chungbuk"]["totalCase"].intValue
let chungnam = json["chungnam"]["totalCase"].intValue
let jeonbuk = json["jeonbuk"]["totalCase"].intValue
let jeonnam = json["jeonnam"]["totalCase"].intValue
let gyeongbuk = json["gyeongbuk"]["totalCase"].intValue
let gyeongnam = json["gyeongnam"]["totalCase"].intValue
let jeju = json["jeju"]["totalCase"].intValue
let quarantine = json["quarantine"]["totalCase"].intValue
self.KoreaCountryData = KCountryData(seoul: seoul, busan: busan, daegu: daegu, incheon: incheon, gwangju: gwangju, daejeon: daejeon, ulsan: ulsan, sejong: sejong, gyeonggi: gyeonggi, gangwon: gangwon, chungbuk: chungbuk, chungnam: chungnam, jeonbuk: jeonbuk, jeonnam: jeonnam, gyeongbuk: gyeongbuk, gyeongnam: gyeongnam, jeju: jeju, quarantine: quarantine)
} else {
self.KoreaCountryData = KcountrytestDate
}
}
struct KCountryData {
let seoul: Int
let busan: Int
let daegu: Int
let incheon: Int
let gwangju: Int
let daejeon: Int
let ulsan: Int
let sejong: Int
let gyeonggi: Int
let gangwon: Int
let chungbuk: Int
let chungnam: Int
let jeonbuk: Int
let jeonnam: Int
let gyeongbuk: Int
let gyeongnam: Int
let jeju: Int
let quarantine: Int
}
I'm using Swiftui to create a covid app.
I got a json form from api and I was pasing
"totalCase": "26,732",
"recovered": "24,395", The json format contains a comma, so it is not accurate output.
ex) totalCase: 26, recovered: 24 I want to erase the comma and express all the numbers.
{
"resultCode": "0",
"resultMessage": "정상 처리되었습니다.",
"korea": {
"countryName": "합계",
"newCase": "97",
"totalCase": "26,732",
"recovered": "24,395",
"death": "468",
"percentage": "51.56",
"newCcase": "79",
"newFcase": "18"
},
"seoul": {
"countryName": "서울",
"newCase": "25",
"totalCase": "6,081",
"recovered": "5,500",
"death": "78",
"percentage": "62.47",
"newCcase": "20",
"newFcase": "5"
}
You can get your string remove the commas (thousand separator) and the enclosing quotes as well. One you have done that you can decode your json string and treat the properties as integers. You can still use SwiftyJSON if you would like to or using Codable as shown bellow:
struct Root: Codable {
let resultCode: Int
let resultMessage: String
let korea, seoul: Country
}
struct Country: Codable {
let countryName: String
let newCase, totalCase, recovered, death: Int
let percentage: Double
let newCcase, newFcase: Int
}
if let result = response.data {
do {
let cleaned = result.replacingOccurrences(of: ",(?=\\d{3})", with: "", options: .regularExpression)
.replacingOccurrences(of: #"\"(\d+)\""#, with: "$1", options: .regularExpression)
.replacingOccurrences(of: #"\"(\d{1,2}.\d{1,2})\""#, with: "$1", options: .regularExpression)
print(cleaned) // { "resultCode": 0, "resultMessage": "정상 처리되었습니다.", "korea": { "countryName": "합계", "newCase": 97, "totalCase": 26732, "recovered": 24395, "death": 468, "percentage": 51.56, "newCcase": 79, "newFcase": 18 }, "seoul": { "countryName": "서울", "newCase": 25, "totalCase": 6081, "recovered": 5500, "death": 78, "percentage": 62.47, "newCcase": 20, "newFcase": 5 }}
let root = try JSONDecoder().decode(Root.self, from: Data(cleaned.utf8))
print(root) // "Root(resultCode: 0, resultMessage: "정상 처리되었습니다.", korea: __lldb_expr_11.Country(countryName: "합계", newCase: 97, totalCase: 26732, recovered: 24395, death: 468, percentage: 51.56, newCcase: 79, newFcase: 18), seoul: __lldb_expr_11.Country(countryName: "서울", newCase: 25, totalCase: 6081, recovered: 5500, death: 78, percentage: 62.47, newCcase: 20, newFcase: 5))\n"
} catch {
print(error)
}
}
You can use a number formatter to convert this format to an int
let numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "en_US")
numberFormatter.numberStyle = .decimal
Example
let string = "6,081"
if let value = numberFormatter.number(from: string)?.intValue {
print(value)
}
I am trying to put the following API link into a Decodable JSON in Swift
Link Here
Below is the code to map the link:
import Foundation
struct SelectedCompany: Decodable {
let LastTradePrice: Double
let ListedCompanyID: String
let ListedCompanyName: String
let MarketTotalOrdersByOrder: Int
let MarketTotalOrdersByPrice: Int
let MarketTotalSharesByOrder: Int
let MarketTotalSharesByPrice: Int
let NumberOfTrades: Int
let Value: Int
let Volume: Int
let MarketAskOrdersByOrder: [Order]
let MarketAskOrdersByPrice: [Order]
let MarketBidOrdersByOrder: [Order]
let MarketBidOrdersByPrice: [Order]
let VWAP: Int
}
struct Order: Decodable {
let Price: Double
let Shares: Int
}
Any idea why is not mapping correctly? When I try to run it in the simulator, I am getting the below error
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
Thanks
Post Edit:
The error comes when I run the below function in a separate class called Service:
class Service {
static func getSpecificCompany(companyID: String, table: UITableView, completion: #escaping (SelectedCompany) -> ()) {
let link = "https://www.adx.ae/en/_vti_bin/ADX/svc/trading.svc/ListedCompanyOrderBook?listedCompanyID=\(companyID)"
guard let url = URL(string: link) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let resultData = data else { return }
do {
let resultCompany = try JSONDecoder().decode(SelectedCompany.self, from: resultData)
DispatchQueue.main.async {
completion(resultCompany)
table.reloadData()
}
} catch {
print(error)
}
}.resume()
}
}
In the ViewController class, the above function will be as follows:
class SomeViewController: UIViewController {
#IBOutlet weak var myTable: UITableView!
var selectedCompany:[SelectedCompany]?
var companyID = String()
override func viewDidLoad() {
super.viewDidLoad()
Service.getSpecificCompany(companyID: companyID, table: myTable) { (company) in
self.selectedCompany = company
}
}
}
The only problem with your structs is the presence of this sort of thing in the JSON:
{"Price":null,"Shares":null}
To cope with that, you need to rewrite your Order struct to allow for null:
struct Order: Decodable {
let Price: Double?
let Shares: Int?
}
As soon as I do that, I can successfully decode the JSON you showed using your SelectedCompany and Order.
Here is playground code that tests (successfully):
let json = """
{"LastTradePrice":3.87,"ListedCompanyID":"ADIB","ListedCompanyName":"Abu Dhabi Islamic Bank","MarketAskOrdersByOrder":[{
"Price":3.87,"Shares":100000},{
"Price":3.88,"Shares":100000},{
"Price":3.88,"Shares":6000},{
"Price":3.89,"Shares":100000},{
"Price":3.9,"Shares":30000},{
"Price":3.9,"Shares":100000},{
"Price":3.98,"Shares":99800},{
"Price":3.98,"Shares":10000},{
"Price":3.99,"Shares":75000},{
"Price":3.99,"Shares":285018},{
"Price":4,"Shares":100000},{
"Price":4,"Shares":100000},{
"Price":4,"Shares":10000},{
"Price":4,"Shares":6336},{
"Price":4,"Shares":100000},{
"Price":4.09,"Shares":5000},{
"Price":4.1,"Shares":50000},{
"Price":4.11,"Shares":5000},{
"Price":4.13,"Shares":28894},{
"Price":4.13,"Shares":25000}],"MarketAskOrdersByPrice":[{
"Price":3.87,"Shares":100000},{
"Price":3.88,"Shares":106000},{
"Price":3.89,"Shares":100000},{
"Price":3.9,"Shares":130000},{
"Price":3.98,"Shares":109800},{
"Price":3.99,"Shares":360018},{
"Price":4,"Shares":316336},{
"Price":4.09,"Shares":5000},{
"Price":4.1,"Shares":50000},{
"Price":4.11,"Shares":5000},{
"Price":4.13,"Shares":53894},{
"Price":4.14,"Shares":193000},{
"Price":4.15,"Shares":85000},{
"Price":4.2,"Shares":127211},{
"Price":4.25,"Shares":250000},{
"Price":4.26,"Shares":10000}],"MarketBidOrdersByOrder":[{
"Price":3.85,"Shares":5349},{
"Price":3.85,"Shares":200000},{
"Price":3.84,"Shares":70000},{
"Price":3.84,"Shares":300000},{
"Price":3.83,"Shares":75000},{
"Price":3.81,"Shares":28000},{
"Price":3.8,"Shares":700000},{
"Price":3.79,"Shares":100000},{
"Price":3.79,"Shares":10000},{
"Price":3.72,"Shares":50000},{
"Price":3.68,"Shares":100000},{
"Price":3.67,"Shares":15000},{
"Price":3.67,"Shares":25000},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null}],"MarketBidOrdersByPrice":[{
"Price":3.85,"Shares":205349},{
"Price":3.84,"Shares":370000},{
"Price":3.83,"Shares":75000},{
"Price":3.81,"Shares":28000},{
"Price":3.8,"Shares":700000},{
"Price":3.79,"Shares":110000},{
"Price":3.72,"Shares":50000},{
"Price":3.68,"Shares":100000},{
"Price":3.67,"Shares":40000},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null},{
"Price":null,"Shares":null}],"MarketTotalOrdersByOrder":28,"MarketTotalOrdersByPrice":16,"MarketTotalSharesByOrder":1678349,"MarketTotalSharesByPrice":1678349,"NumberOfTrades":41,"VWAP":0,"Value":7159043,"Volume":1863558}
"""
let jsondata = json.data(using: .utf8)
struct SelectedCompany: Decodable {
let LastTradePrice: Double
let ListedCompanyID: String
let ListedCompanyName: String
let MarketTotalOrdersByOrder: Int
let MarketTotalOrdersByPrice: Int
let MarketTotalSharesByOrder: Int
let MarketTotalSharesByPrice: Int
let NumberOfTrades: Int
let Value: Int
let Volume: Int
let MarketAskOrdersByOrder: [Order]
let MarketAskOrdersByPrice: [Order]
let MarketBidOrdersByOrder: [Order]
let MarketBidOrdersByPrice: [Order]
let VWAP: Int
}
struct Order: Decodable {
let Price: Double?
let Shares: Int?
}
do {
let result = try JSONDecoder().decode(SelectedCompany.self, from: jsondata!)
print(result) // works fine
} catch {
print(error) // no error
}