import Foundation
class ReadLocalJSON {
static func readJSONFromFile(fileName: String) -> JSON
{
var json: JSON
if let path = Bundle.main.path(forResource: fileName, ofType: "json") {
do {
let fileUrl = URL(fileURLWithPath: path)
let data = try Data(contentsOf: fileUrl, options: .mappedIfSafe)
json = try? JSONSerialization.jsonObject(with: data)
} catch {
print("Something goes wrong when reading local json file.")
}
}
return json
}
}
I try to read the local json file and output json. But the line json = try? JSONSerialization.jsonObject(with: data) gives an error saying Cannot assign value of type 'Any?' to type 'JSON'.
My json data looks like
{
"leagues":
[
{ "name": "Hockey",
"image": "hockey",
"games":
[
{
"game_state": "Final",
"game_time": 1456662600,
"home_team_city": "Alberta",
"home_team_name": "Pigs",
"home_team_score": 1,
"home_team_logo": "pig",
"visit_team_city": "Montreal",
"visit_team_name": "Fishes",
"visit_team_score": 4,
"visit_team_logo": "fish"
}
]
}
]
}
When I change the output type to be Any? I print the output and it seems missing some elements.
{
leagues = (
{
games = (
{
"game_state" = Final;
"game_time" = 1456662600;
...
How can I fix it?
Check the solution below, I used Codable for the JSON decoding.
import Foundation
struct Sports: Codable {
let leagues: [League]
}
struct League: Codable {
let name, image: String
let games: [Game]
}
struct Game: Codable {
let gameState: String
let gameTime: Int
let homeTeamCity, homeTeamName: String
let homeTeamScore: Int
let homeTeamLogo, visitTeamCity, visitTeamName: String
let visitTeamScore: Int
let visitTeamLogo: String
enum CodingKeys: String, CodingKey {
case gameState = "game_state"
case gameTime = "game_time"
case homeTeamCity = "home_team_city"
case homeTeamName = "home_team_name"
case homeTeamScore = "home_team_score"
case homeTeamLogo = "home_team_logo"
case visitTeamCity = "visit_team_city"
case visitTeamName = "visit_team_name"
case visitTeamScore = "visit_team_score"
case visitTeamLogo = "visit_team_logo"
}
}
class ReadLocalJSON {
static func readJSONFromFile(fileName: String) -> Sports?
{
let path = Bundle.main.path(forResource: fileName, ofType: "json")
let url = URL(fileURLWithPath: path!)
let sportsData = try? Data(contentsOf: url)
guard
let data = sportsData
else { return nil }
do {
let result = try JSONDecoder().decode(Sports.self, from: data)
print(result)
return result
} catch let error {
print("Failed to Decode Object", error)
return nil
}
}
}
ReadLocalJSON.readJSONFromFile(fileName: "test")
Step 1:- first make a modal class in your project
struct Welcome: Codable {
let leagues: [League]?
}
// MARK: - League
struct League: Codable {
let name, image: String?
let games: [Game]?
}
// MARK: - Game
struct Game: Codable {
let gameState: String?
let gameTime: Int?
let homeTeamCity, homeTeamName: String?
let homeTeamScore: Int?
let homeTeamLogo, visitTeamCity, visitTeamName: String?
let visitTeamScore: Int?
let visitTeamLogo: String?
enum CodingKeys: String, CodingKey {
case gameState = "game_state"
case gameTime = "game_time"
case homeTeamCity = "home_team_city"
case homeTeamName = "home_team_name"
case homeTeamScore = "home_team_score"
case homeTeamLogo = "home_team_logo"
case visitTeamCity = "visit_team_city"
case visitTeamName = "visit_team_name"
case visitTeamScore = "visit_team_score"
case visitTeamLogo = "visit_team_logo"
}
}
Step 2 : - After getting response write this line,
let decoder = JSONDecoder()
let obj = try! decoder.decode(Welcome.self, from: jsonData!)
IF you have still problem let me know
Related
I parsed data from a json file, but I don't know how to get these variables.
Need charcode, name and value.
I need to display them in a table using swiftui. I got a mess in the console and I don't know how to get to this data
this is struct
import Foundation
struct CurrencyModel: Codable {
let valute: [String : Valute]
enum CodingKeys: String, CodingKey {
case valute = "Valute"
}
}
struct Valute: Codable {
let charCode, name: String
let value: Double
enum CodingKeys: String, CodingKey {
case charCode = "CharCode"
case name = "Name"
case value = "Value"
}
}
and this is parser
class FetchDataVM: ObservableObject {
var valueData = [String : Any]()
init() {
fetchCurrency()
}
func fetchCurrency() {
let urlString = "https://www.cbr-xml-daily.ru/daily_json.js"
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!) {data, _, error in
DispatchQueue.main.async {
if let data = data {
do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(CurrencyModel.self, from: data)
print(decodedData)
} catch {
print("Error! Something went wrong.")
}
}
}
}.resume()
}
}
As all needed information is in the Valute struct you need only the values of the valute dictionary. Replace
var valueData = [String : Any]()
with
#Published var valueData = [Valute]()
and after the line print(decodedData) insert
self.valueData = decodedData.valute.values.sorted{$0.name < $1.name}
or
self.valueData = decodedData.valute.values.sorted{$0.charCode < $1.charCode}
In the view you can iterate the array simply with a ForEach expression
I try to deserialize my JSON by using Decodable protocol, also i use enum with CodingKey, but it doesn't work. I need only nested array (start with "indicator"), and only few fields (all of them in struct). I tried a lot of different options, but unfortunately..
P.S. Also i tried to do it without CodingKey. Anyway response was: "Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "country", intValue: nil)" Ofc i read this, maybe array is a reason(i mean this strange intValue)?
JSON
[
{
"page":1,
"pages":2,
"per_page":50,
"total":59,
"sourceid":"2",
"lastupdated":"2019-03-21"
},
[
{
"indicator":{
"id":"IP.PAT.RESD",
"value":"Patent applications, residents"
},
"country":{
"id":"SS",
"value":"South Sudan"
},
"countryiso3code":"SSD",
"date":"2018",
"value":null,
"unit":"",
"obs_status":"",
"decimal":0
},
{
"indicator":{
"id":"IP.PAT.RESD",
"value":"Patent applications, residents"
},
"country":{
"id":"SS",
"value":"South Sudan"
},
"countryiso3code":"SSD",
"date":"2017",
"value":null,
"unit":"",
"obs_status":"",
"decimal":0
},
...
]
]
My code
struct CountryObject: Decodable{
var country: CountryInfo
var date: Int
var value: Int?
private enum RawValues: String, Decodable{
case date = "date"
case vallue = "value"
}
}
struct CountryInfo: Decodable{//Country names
var id: String?
var value: String?
private enum RawValues: String, Decodable{
case id = "id"
case value = "value"
}
}//
let urlString = "https://api.worldbank.org/v2/country/SS/indicator/IP.PAT.RESD?format=json"
guard let url = URL(string: urlString) else {return}
URLSession.shared.dataTask(with: url) {(data,response,error) in
guard let data = data else {return}
guard error == nil else {return}
do{
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let countryObject = try! decoder.decode([CountryObject].self, from: data)
print(countryObject)
}catch let error{
print(error)
}
}.resume()
Create a root struct and decode the array with unkeyedContainer
struct Root : Decodable {
let info : Info
let countryObjects : [CountryObject]
init(from decoder: Decoder) throws {
var arrayContrainer = try decoder.unkeyedContainer()
info = try arrayContrainer.decode(Info.self)
countryObject = try arrayContrainer.decode([CountryObject].self)
}
}
struct Info : Decodable {
let page, pages, perPage: Int
let lastupdated: String
}
struct CountryObject : Decodable {
let country: CountryInfo
let date: String
let value: Int?
}
struct CountryInfo : Decodable { //Country names
let id: String
let value: String
}
...
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let root = try decoder.decode(Root.self, from: data)
let countryObjects = root.countryObjects
print(countryObjects)
} catch { print(error) }
(De)serializing the JSON twice is unnecessarily expensive.
I was trying to make a weather app. And having a problem using SwiftyJSON.
I need to assign [WeatherModel] to my JSON data.
Basically, I need to set json variable to weatherData. The code is below.
Here is my controller:
var weatherData = [WeatherModel]()
func getJSONData(completed: #escaping () -> ()) {
if let filepath = Bundle.main.path(forResource: "weather", ofType: "json") {
do{
let data = try Data(contentsOf: URL(fileURLWithPath: filepath), options: .alwaysMapped)
let json = JSON(data: data)
// And here I need to set json to weatherData
} catch let error{
print(error.localizedDescription)
}
DispatchQueue.main.async {
completed()
}
} else {
print("file not found")
}
}
Here is my WeatherModel struct:
struct WeatherModel {
let cod: String
let message: Double
let cnt: Int
let list: [List]
let city: City
}
Note: I really need this to be made using only SwiftJSON. Any help will be appreciated :]
Well, we don't know what your JSON looks like.
To provide a sample, if this is what your JSON looked like:
{
"data": [
{
"cod": "some string here",
"message": 2.0,
"cnt": 1
...
}
]
}
... you would decode it as follows:
for (_, dict) in json["data"] {
guard let cod = dict["cod"].string else { continue }
guard let message = dict["message"].double else { continue }
guard let cnt = dict["cnt"].int else { continue }
// ...
let weather = WeatherModel(cod: cod, message: message, cnt: cnt, ...)
weatherData.append(weather)
}
You would have to modify this to work with your json format and the exact requirements.
Try this, i am not sure about your json structure correctly.
struct WeatherModel:Codable {
let cod: String
let message: Double
let cnt: Int
let list: [List]
let city: City
enum CodingKeys: String, CodingKey
{
case title = "name"
case url = "message"
case cnt
case list
case city
}
}
struct City:Codable {
let name
}
struct List:Codable {
//define your list data as in json
}
after this decode your json data.
if let wheatherData = try? JSONDecoder().decode(WeatherModel.self, from: data) {
// here Is your json model weatherData
}
I got to know struct "Codable" in swift 4.0, *.
So, I tried that when decode josn.
if let jsonData = jsonString.data(using: .utf8) {
let decodingData = try? JSONDecoder().decode(SampleModel.self, from: jsonData)
}
Example sample data model below.
struct SampleModel : Codable {
var no: Int?
var category: Int?
var template_seq: Int?
}
And sample json data is .. below.
{
"data": {
"result" : 1
"total_count": 523,
"list": [
{
"no": 16398,
"category" : 23,
"template_seq" : 1
},
{
"no": -1,
"category" : 23,
"template_seq" : 1
}
]
}
}
But i want filtering wrong data.
If the value of "no" is less than or equal to 0, it is an invalid value.
Before not using codable...below.
(using Alamifre ison response )
guard let dictionaryData = responseJSON as? [String : Any] else { return nil }
guard let resultCode = dictionaryData["result"] as? Bool , resultCode == true else { return nil }
guard let theContainedData = dictionaryData["data"] as? [String:Any] else { return nil }
guard let sampleListData = theContainedData["list"] as? [[String : Any]] else { return nil }
var myListData = [MyEstimateListData]()
for theSample in sampleListData {
guard let existNo = theSample["no"] as? Int, existNo > 0 else {
continue
}
myListData.append( ... )
}
return myListData
how to filter wrong data or invalid data using swift 4.0 Codable ??
you can make codable for inital resonse
Here is your model:
import Foundation
struct Initial: Codable {
let data: DataModel?
}
struct DataModel: Codable {
let result, totalCount: Int
let list: [List]?
enum CodingKeys: String, CodingKey {
case result
case totalCount = "total_count"
case list
}
}
struct List: Codable {
let no, category, templateSeq: Int
enum CodingKeys: String, CodingKey {
case no, category
case templateSeq = "template_seq"
}
}
extension Initial {
init(data: Data) throws {
self = try JSONDecoder().decode(Initial.self, from: data)
}
}
And use it like that :
if let initail = try? Initial.init(data: data) , let list = initail.data?.list {
var myListData = list.filter { $0.no > 0 }
}
Yes, you have to use filters for that with codable:
1: Your struct should be according to your response like that:
struct SampleModel : Codable {
var result: Int?
var total_count: Int?
var list: [List]?
}
struct List : Codable {
var no: Int?
var category: Int?
var template_seq: Int?
}
2: Parse your response using a codable struct like that:
do {
let jsonData = try JSONSerialization.data(withJSONObject: dictionaryData["data"] as Any, options: JSONSerialization.WritingOptions.prettyPrinted)
let resultData = try JSONDecoder().decode(SampleModel.self, from: jsonData)
success(result as AnyObject)
} catch let message {
print("JSON serialization error:" + "\(message)")
}
3: now you can filter invalid data simply:
let filterListData = resultData.list?.filter({$0.no > 0})
let invalidData = resultData.list?.filter({$0.no <= 0})
I want to parse a Json file with this structure:
{"x":"exchange","b":"usd","ds":["exchange","avgp","mcap","ppc7D","ppc12h","ppc4h","ppc24h"],"data":[["Dow Jones","16360.447","273.89B","6.62","2.14","-0.59","-1.99"],["Dax","877.422","6.80B","38.15","-7.4","-4.44","-4.12"],["nikkei","30.077","2.96B","24.22","-2.3","-4.02","-4.95"],["ATX","281.509","15.29B","214.97","-5.48","-4.58","-10.77"]]}
I do not know how to parse a Json file where there is no definition like:
exchange = "Dow Jones", avgp = 16360.477" etc.
And i could not found anything online.
My code looks like this:
let json = """
{"x":"exchange","b":"usd","ds":["exchange","avgp","mcap","ppc7D","ppc12h","ppc4h","ppc24h"],"data":[["Dow Jones","16360.447","273.89B","6.62","2.14","-0.59","-1.99"],["Dax","877.422","6.80B","38.15","-7.4","-4.44","-4.12"],["nikkei","30.077","2.96B","24.22","-2.3","-4.02","-4.95"],["ATX","281.509","15.29B","214.97","-5.48","-4.58","-10.77"]]}
""".data(using: .utf8)!
struct JsonWebsocket: Decodable {
let exchange: String
let avgp: String
let mcap: String
let ppc7D: String
let ppc12h: String
let ppc4h: String
let ppc24h: String
init(exchange: String, avgp: String, mcap: String, ppc7D: String, ppc12h: String, ppc4h: String, ppc24h: String) {
self.exchange = exchange
self.avgp = avgp
self.mcap = mcap
self.ppc7D = ppc7D
self.ppc12h = ppc24h
self.ppc4h = ppc4h
self.ppc24h = ppc24h
}
}
func fetchJson() -> [String:JsonWebsocket] {
let jsonCoinsDecode = json
let coinDecode = JSONDecoder()
let output = try! coinDecode.decode([String:JsonWebsocket].self, from: jsonCoinsDecode)
return output
}
let array = fetchDataTradingPairs()
But of course it returns an error as the structure does not match the json file.
Does anyone know how to parse this json?
Thanks!
create a struct like that.
struct JsonWebsocket: Decodable {
let x: String
let b: String
let ds: [String]
let data: [[String]]
}
and decode
do {
let coinDecode = JSONDecoder()
let output = try coinDecode.decode(JsonWebsocket.self, from: json)
print(output.data)
}
catch let error{
print(error.localizedDescription)
}
alter native
otherwise, create a custom
struct JsonWebsocket: Decodable {
let exchange: String
let avgp: String
let mcap: String
let ppc7D: String
let ppc12h: String
let ppc4h: String
let ppc24h: String
init(json: [String]) {
self.exchange = json[0]
self.avgp = json[1]
self.mcap = json[2]
self.ppc7D = json[3]
self.ppc12h = json[4]
self.ppc4h = json[5]
self.ppc24h = json[6]
}
}
do {
let jsonData = try JSONSerialization.jsonObject(with: json, options: []) as? [String: Any]
var jsonWebsocket: [JsonWebsocket] = []
if let data = jsonData!["data"] as? [[String]] {
for d in data {
jsonWebsocket.append(JsonWebsocket(json: d))
}
}
print(jsonWebsocket.count)
}
catch let error{
print(error.localizedDescription)
}