I am facing some issues when mapping my JSON response to the model. After getting the error printed it says the 'type mismatch' although I created a model class from quicktype.io. Here is my model class. I have read many questions about the same title, but I dont find any help.
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
// let responseCustomerInfiormation = try? newJSONDecoder().decode(ResponseCustomerInfiormation.self, from: jsonData)
import Foundation
// MARK: - ResponseCustomerInfiormation
class ResponseCustomerInfiormation: Codable {
let status: Bool?
let message: String?
let responseDescription: ResponseDescription?
let encryptStatus: Bool?
enum CodingKeys: String, CodingKey {
case status = "Status"
case message = "Message"
case responseDescription = "ResponseDescription"
case encryptStatus = "EncryptStatus"
}
required init(from decoder: Decoder) throws{
let values = try decoder.container(keyedBy: CodingKeys.self)
status = try values.decodeIfPresent(Bool.self,forKey: .status)
message = try values.decodeIfPresent(String.self,forKey: .message)
responseDescription = try values.decodeIfPresent(ResponseDescription.self,forKey: .responseDescription)
encryptStatus = try values.decodeIfPresent(Bool.self, forKey: .encryptStatus)
}
class ResponseDescription: Codable {
let id: Int?
let customerWebID: Int?
let customerWalletID: Int?
let cellNo: String?
let firstName: String?
let lastName: String?
let dateOfBirth: String?
let pincode: String?
let password: String?
let cnic: String?
let emiratesExpiry: String?
let email: String?
let walletCard: String?
let kkCard: String?
let country: String?
let countryID: Int?
let countryOfBirth: String?
let homeCountryCellNo: String?
let city: String?
let cityID: Int?
let residentOrVisitor: Int?
let nationalityID: Int?
let nationality: String?
let profilePicture: String?
let regionID: Int?
let address: String?
let themeColor: Int?
let vendorID: Int?
let vendorImage: String?
let sahulatWallet: String?
let karachiKingWallet: String?
let qrCode: String?
let valueBack: String?
let promotionalValueBack: String?
let aryCoin: String?
let lockedTopup: String?
let sahulatComitiAmount: String?
let sonaComitiAmount: String?
let sonaComitiGold: String?
let milliGoldRedeemable: String?
let milliGoldUnRedeemable: String?
let milliGoldAmountUnRedeemable: String?
let sahulatWalletCardRegistrationFeePaid: String?
let karachiKingCardRegistrationFeePaid: String?
let totalMonthlyFeeDueSahulatCard: String?
let totalMonthlyFeeDueKKCard: String?
let totalMonthlyFeePaid: String?
let totalMonthlyFeeDue: String?
let dollarDealPurchaseAmount: String?
let dollarDealValueBack: String?
let dollarDealPromotionalValueBack: String?
let tokenNo: String?
let encryptionKey: String?
let responseStatus: Bool?
let responseMessage: String?
enum CodingKeys: String, CodingKey {
case id = "ID"
case customerWebID = "CustomerWebID"
case customerWalletID = "CustomerWalletID"
case cellNo = "CellNo"
case firstName = "FirstName"
case lastName = "LastName"
case dateOfBirth = "DateOfBirth"
case pincode = "Pincode"
case password = "Password"
case cnic = "CNIC"
case emiratesExpiry = "EmiratesExpiry"
case email = "Email"
case walletCard = "WalletCard"
case kkCard = "KKCard"
case country = "Country"
case countryID = "CountryID"
case countryOfBirth = "CountryOfBirth"
case homeCountryCellNo = "HomeCountryCellNo"
case city = "City"
case cityID = "CityID"
case residentOrVisitor = "ResidentOrVisitor"
case nationalityID = "NationalityID"
case nationality = "Nationality"
case profilePicture = "ProfilePicture"
case regionID = "RegionID"
case address = "Address"
case themeColor = "ThemeColor"
case vendorID = "VendorID"
case vendorImage = "VendorImage"
case sahulatWallet = "SahulatWallet"
case karachiKingWallet = "KarachiKingWallet"
case qrCode = "QRCode"
case valueBack = "ValueBack"
case promotionalValueBack = "PromotionalValueBack"
case aryCoin = "ARYCoin"
case lockedTopup = "LockedTopup"
case sahulatComitiAmount = "SahulatComitiAmount"
case sonaComitiAmount = "SonaComitiAmount"
case sonaComitiGold = "SonaComitiGold"
case milliGoldRedeemable = "MilliGoldRedeemable"
case milliGoldUnRedeemable = "MilliGoldUnRedeemable"
case milliGoldAmountUnRedeemable = "MilliGoldAmountUnRedeemable"
case sahulatWalletCardRegistrationFeePaid = "SahulatWalletCardRegistrationFeePaid"
case karachiKingCardRegistrationFeePaid = "KarachiKingCardRegistrationFeePaid"
case totalMonthlyFeeDueSahulatCard = "TotalMonthlyFeeDueSahulatCard"
case totalMonthlyFeeDueKKCard = "TotalMonthlyFeeDueKKCard"
case totalMonthlyFeePaid = "TotalMonthlyFeePaid"
case totalMonthlyFeeDue = "TotalMonthlyFeeDue"
case dollarDealPurchaseAmount = "DollarDealPurchaseAmount"
case dollarDealValueBack = "DollarDealValueBack"
case dollarDealPromotionalValueBack = "DollarDealPromotionalValueBack"
case tokenNo = "TokenNo"
case encryptionKey = "EncryptionKey"
case responseStatus = "ResponseStatus"
case responseMessage = "ResponseMessage"
}
required init(from decoder: Decoder) throws{
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decodeIfPresent(Int.self, forKey: .id)
customerWebID = try values.decodeIfPresent(Int.self, forKey: .customerWebID)
customerWalletID = try values.decodeIfPresent(Int.self, forKey: .customerWalletID)
cellNo = try values.decodeIfPresent(String.self, forKey: .cellNo)
firstName = try values.decodeIfPresent(String.self, forKey: .firstName)
lastName = try values.decodeIfPresent(String.self, forKey: .lastName)
dateOfBirth = try values.decodeIfPresent(String.self, forKey: .dateOfBirth)
pincode = try values.decodeIfPresent(String.self, forKey: .pincode)
password = try values.decodeIfPresent(String.self, forKey: .password)
cnic = try values.decodeIfPresent(String.self, forKey: .cnic)
emiratesExpiry = try values.decodeIfPresent(String.self, forKey: .emiratesExpiry)
email = try values.decodeIfPresent(String.self, forKey: .email)
walletCard = try values.decodeIfPresent(String.self, forKey: .walletCard)
kkCard = try values.decodeIfPresent(String.self, forKey: .kkCard)
country = try values.decodeIfPresent(String.self, forKey: .country)
countryID = try values.decodeIfPresent(Int.self, forKey: .countryID)
countryOfBirth = try values.decodeIfPresent(String.self, forKey: .countryOfBirth)
homeCountryCellNo = try values.decodeIfPresent(String.self, forKey: .homeCountryCellNo)
city = try values.decodeIfPresent(String.self, forKey: .city)
cityID = try values.decodeIfPresent(Int.self, forKey: .cityID)
residentOrVisitor = try values.decodeIfPresent(Int.self, forKey: .residentOrVisitor)
nationalityID = try values.decodeIfPresent(Int.self, forKey: .nationalityID)
nationality = try values.decodeIfPresent(String.self, forKey: .nationality)
profilePicture = try values.decodeIfPresent(String.self, forKey: .profilePicture)
regionID = try values.decodeIfPresent(Int.self, forKey: .regionID)
address = try values.decodeIfPresent(String.self, forKey: .address)
themeColor = try values.decodeIfPresent(Int.self, forKey: .themeColor)
vendorID = try values.decodeIfPresent(Int.self, forKey: .vendorID)
vendorImage = try values.decodeIfPresent(String.self, forKey: .vendorImage)
sahulatWallet = try values.decodeIfPresent(String.self, forKey: .sahulatWallet)
karachiKingWallet = try values.decodeIfPresent(String.self, forKey: .karachiKingWallet)
qrCode = try values.decodeIfPresent(String.self, forKey: .qrCode)
valueBack = try values.decodeIfPresent(String.self, forKey: .valueBack)
promotionalValueBack = try values.decodeIfPresent(String.self, forKey: .promotionalValueBack)
aryCoin = try values.decodeIfPresent(String.self, forKey: .aryCoin)
lockedTopup = try values.decodeIfPresent(String.self, forKey: .lockedTopup)
sahulatComitiAmount = try values.decodeIfPresent(String.self, forKey: .sahulatComitiAmount)
sonaComitiAmount = try values.decodeIfPresent(String.self, forKey: .sonaComitiAmount)
sonaComitiGold = try values.decodeIfPresent(String.self, forKey: .sonaComitiGold)
milliGoldRedeemable = try values.decodeIfPresent(String.self, forKey: .milliGoldRedeemable)
milliGoldUnRedeemable = try values.decodeIfPresent(String.self, forKey: .milliGoldUnRedeemable)
milliGoldAmountUnRedeemable = try values.decodeIfPresent(String.self, forKey: .milliGoldAmountUnRedeemable)
sahulatWalletCardRegistrationFeePaid = try values.decodeIfPresent(String.self, forKey: .sahulatWalletCardRegistrationFeePaid)
karachiKingCardRegistrationFeePaid = try values.decodeIfPresent(String.self, forKey: .karachiKingCardRegistrationFeePaid)
totalMonthlyFeeDueSahulatCard = try values.decodeIfPresent(String.self, forKey: .totalMonthlyFeeDueSahulatCard)
totalMonthlyFeeDueKKCard = try values.decodeIfPresent(String.self, forKey: .totalMonthlyFeeDueKKCard)
totalMonthlyFeePaid = try values.decodeIfPresent(String.self, forKey: .totalMonthlyFeePaid)
totalMonthlyFeeDue = try values.decodeIfPresent(String.self, forKey: .totalMonthlyFeeDue)
dollarDealPurchaseAmount = try values.decodeIfPresent(String.self, forKey: .dollarDealPurchaseAmount)
dollarDealValueBack = try values.decodeIfPresent(String.self, forKey: .dollarDealValueBack)
dollarDealPromotionalValueBack = try values.decodeIfPresent(String.self, forKey: .dollarDealPromotionalValueBack)
tokenNo = try values.decodeIfPresent(String.self, forKey: .tokenNo)
encryptionKey = try values.decodeIfPresent(String.self, forKey: .encryptionKey)
responseStatus = try values.decodeIfPresent(Bool.self, forKey: .responseStatus)
responseMessage = try values.decodeIfPresent(String.self, forKey: .responseMessage)
}
}
}
// MARK: - ResponseDescription
And the JSON I am receiving is
{
"Status": true,
"Message": "Customer Information Found",
"ResponseDescription": {
"ID": 11111,
"CustomerWebID": 324,
"CustomerWalletID": 1234,
"CellNo": "",
"FirstName": "mujtuba",
"LastName": " Amin",
"DateOfBirth": "19000-01-01",
"Pincode": "8120",
"Password": "",
"CNIC": "",
"EmiratesExpiry": "",
"Email": "",
"WalletCard": "",
"KKCard": "",
"Country": "United Arab Emirates",
"CountryID": 0,
"CountryOfBirth": "",
"HomeCountryCellNo": "00",
"City": "",
"CityID": 0,
"ResidentOrVisitor": 1,
"NationalityID": 0,
"Nationality": "Pakistani",
"ProfilePicture": " ",
"RegionID": 2,
"Address": "dd",
"ThemeColor": 0,
"VendorID": 0,
"VendorImage": "",
"SahulatWallet": "1",
"KarachiKingWallet": "0",
"QRCode": "https://chart.googleapis.com/chart?chs=400x400&cht=qr&chld=h&chl=970657|2|2345|941A3E84-B1EB-4B9B-9141-71227844A5B9",
"ValueBack": "3",
"PromotionalValueBack": "3",
"ARYCoin": "1000",
"LockedTopup": "0",
"SahulatComitiAmount": "0",
"SonaComitiAmount": "0",
"SonaComitiGold": "0",
"MilliGoldRedeemable": "3656",
"MilliGoldUnRedeemable": "0",
"MilliGoldAmountUnRedeemable": "0",
"SahulatWalletCardRegistrationFeePaid": "0",
"KarachiKingCardRegistrationFeePaid": "0",
"TotalMonthlyFeeDueSahulatCard": "0",
"TotalMonthlyFeeDueKKCard": "0",
"TotalMonthlyFeePaid": "0",
"TotalMonthlyFeeDue": "0",
"DollarDealPurchaseAmount": "0",
"DollarDealValueBack": "0",
"DollarDealPromotionalValueBack": "0",
"TokenNo": "0",
"EncryptionKey": "",
"ResponseStatus": true,
"ResponseMessage": "Customer Details Found"
},
"EncryptStatus": true
}
Can anyone please guide me that where I am having error or which part of code is making issue.
your json response is not in correct format.
"CustomerWalletID": 0000 // this is causing problem
"CustomerWalletID": 0, // try this
Reference :- This will show you if your json is valid or not
There is a problem with initializer I guess, do try this type of initializer
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decodeIfPresent(String.self, forKey: .name)
...
}
Based on the requirement I got two different kinds of response from api. That is
{
"shopname":"xxx",
"quantity":4,
"id":1,
"price":200.00,
}
another response
{
"storename":"xxx",
"qty":4,
"id":1,
"amount":200.00,
}
Here both json values are decoding in same model class. Kindly help me to resolve this concern.
is it is possible to set value in single variable like qty and quantity both are stored in same variable based on key param availability
Here's an approach that lets you have only one property in your code, instead of two Optionals:
Define a struct that contains all the properties you need, with the names that you'd like to use in your code. Then, define two CodingKey enums that map those properties to the two different JSON formats and implement a custom initializer:
let json1 = """
{
"shopname":"xxx",
"quantity":4,
"id":1,
"price":200.00,
}
""".data(using: .utf8)!
let json2 = """
{
"storename":"xxx",
"qty":4,
"id":1,
"amount":200.00,
}
""".data(using: .utf8)!
struct DecodingError: Error {}
struct Model: Decodable {
let storename: String
let quantity: Int
let id: Int
let price: Double
enum CodingKeys1: String, CodingKey {
case storename = "shopname"
case quantity
case id
case price
}
enum CodingKeys2: String, CodingKey {
case storename
case quantity = "qty"
case id
case price = "amount"
}
init(from decoder: Decoder) throws {
let container1 = try decoder.container(keyedBy: CodingKeys1.self)
let container2 = try decoder.container(keyedBy: CodingKeys2.self)
if let storename = try container1.decodeIfPresent(String.self, forKey: CodingKeys1.storename) {
self.storename = storename
self.quantity = try container1.decode(Int.self, forKey: CodingKeys1.quantity)
self.id = try container1.decode(Int.self, forKey: CodingKeys1.id)
self.price = try container1.decode(Double.self, forKey: CodingKeys1.price)
} else if let storename = try container2.decodeIfPresent(String.self, forKey: CodingKeys2.storename) {
self.storename = storename
self.quantity = try container2.decode(Int.self, forKey: CodingKeys2.quantity)
self.id = try container2.decode(Int.self, forKey: CodingKeys2.id)
self.price = try container2.decode(Double.self, forKey: CodingKeys2.price)
} else {
throw DecodingError()
}
}
}
do {
let j1 = try JSONDecoder().decode(Model.self, from: json1)
print(j1)
let j2 = try JSONDecoder().decode(Model.self, from: json2)
print(j2)
} catch {
print(error)
}
Handling different key names in single model
Below are two sample json(dictionaries) that have some common keys (one, two) and a few different keys (which serve the same purpose of error).
Sample json:
let error_json:[String: Any] = [
"error_code": 404, //different
"error_message": "file not found", //different
"one":1, //common
"two":2 //common
]
let failure_json:[String: Any] = [
"failure_code": 404, //different
"failure_message": "file not found", //different
"one":1, //common
"two":2 //common
]
CommonModel
struct CommonModel : Decodable {
var code: Int?
var message: String?
var one:Int //common
var two:Int? //common
private enum CodingKeys: String, CodingKey{ //common
case one, two
}
private enum Error_CodingKeys : String, CodingKey {
case code = "error_code", message = "error_message"
}
private enum Failure_CodingKeys : String, CodingKey {
case code = "failure_code", message = "failure_message"
}
init(from decoder: Decoder) throws {
let commonValues = try decoder.container(keyedBy: CodingKeys.self)
let errors = try decoder.container(keyedBy: Error_CodingKeys.self)
let failures = try decoder.container(keyedBy: Failure_CodingKeys.self)
///common
self.one = try commonValues.decodeIfPresent(Int.self, forKey: .one)!
self.two = try commonValues.decodeIfPresent(Int.self, forKey: .two)
/// different
if errors.allKeys.count > 0{
self.code = try errors.decodeIfPresent(Int.self, forKey: .code)
self.message = try errors.decodeIfPresent(String.self, forKey: .message)
}
if failures.allKeys.count > 0{
self.code = try failures.decodeIfPresent(Int.self, forKey: .code)
self.message = try failures.decodeIfPresent(String.self, forKey: .message)
}
}
}
Below extension will help you to convert your dictionary to data.
public extension Decodable {
init(from: Any) throws {
let data = try JSONSerialization.data(withJSONObject: from, options: .prettyPrinted)
let decoder = JSONDecoder()
self = try decoder.decode(Self.self, from: data)
}
}
Testing
public func Test_codeble(){
do {
let err_obj = try CommonModel(from: error_json)
print(err_obj)
let failed_obj = try CommonModel(from: failure_json)
print(failed_obj)
}catch let error {
print(error.localizedDescription)
}
}
Use like
struct modelClass : Codable {
let amount : Float?
let id : Int?
let price : Float?
let qty : Int?
let quantity : Int?
let shopname : String?
let storename : String?
enum CodingKeys: String, CodingKey {
case amount = "amount"
case id = "id"
case price = "price"
case qty = "qty"
case quantity = "quantity"
case shopname = "shopname"
case storename = "storename"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
amount = try values.decodeIfPresent(Float.self, forKey: .amount)
id = try values.decodeIfPresent(Int.self, forKey: .id)
price = try values.decodeIfPresent(Float.self, forKey: .price)
qty = try values.decodeIfPresent(Int.self, forKey: .qty)
quantity = try values.decodeIfPresent(Int.self, forKey: .quantity)
shopname = try values.decodeIfPresent(String.self, forKey: .shopname)
storename = try values.decodeIfPresent(String.self, forKey: .storename)
}
}
I am wanting to parse the weather data from the Met Office for Plymouth. The structure that I have is the following:
struct WeatherRoot: Codable {
var siteRep: SiteRep
private enum CodingKeys: String, CodingKey {
case siteRep = "SiteRep"
}
}
struct SiteRep: Codable {
var dataWx: DataWx
var dataDV: DataDV
private enum CodingKeys: String, CodingKey {
case dataWx = "Wx"
case dataDV = "DV"
}
}
struct DataWx: Codable {
var param: [Param]?
private enum CodingKeys: String, CodingKey {
case param = "Param"
}
}
struct Param: Codable {
var headings: WeatherDataHeadings
private enum CodingKeys: String, CodingKey {
case headings = "Param"
}
}
struct WeatherDataHeadings: Codable {
var name: String
var unit: String
var title: String
private enum CodingKeys: String, CodingKey {
case name = "name"
case unit = "units"
case title = "$"
}
}
struct DataDV: Codable {
var dataDate: String
var type: String
var location: LocationDetails
private enum CodingKeys: String, CodingKey {
case dataDate = "dataType"
case type = "type"
case location = "Location"
}
}
struct LocationDetails: Codable {
var id: String
var latitude: String
var longitude: String
var name: String
var country: String
var continent: String
var elevation: String
var period: [Period]
private enum CodingKeys: String, CodingKey {
case id = "i"
case latitude = "lat"
case longitude = "lon"
case name
case country
case continent
case elevation
case period = "Period"
}
}
struct Period: Codable {
var type: String
var value: String
var rep: [Rep]
private enum CodingKeys: String, CodingKey {
case type = "type"
case value = "value"
case rep = "Rep"
}
}
struct Rep: Codable {
var windDirection: String
var feelsLikeTemperature: String
var windGust: String
var humidity: String
var precipitation: String
var windSpeed: String
var temperature: String
var visibility: String
var weatherType: String
var uvIndex: String
var time: String
private enum CodingKeys: String, CodingKey {
case windDirection = "D"
case feelsLikeTemperature = "F"
case windGust = "G"
case humidity = "H"
case precipitation = "Pp"
case windSpeed = "S"
case temperature = "T"
case visibility = "V"
case weatherType = "W"
case uvIndex = "U"
case time = "$"
}
}
extension WeatherRoot {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
siteRep = try values.decode(SiteRep.self, forKey: .siteRep)
}
}
extension SiteRep {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
dataWx = try values.decode(DataWx.self, forKey: .dataWx)
dataDV = try values.decode(DataDV.self, forKey: .dataDV)
}
}
extension DataWx {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
param = try values.decodeIfPresent([Param].self, forKey: .param)
}
}
extension Param {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
headings = try values.decode(WeatherDataHeadings.self, forKey: .headings)
}
}
extension WeatherDataHeadings {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
unit = try values.decode(String.self, forKey: .unit)
title = try values.decode(String.self, forKey: .title)
}
}
extension DataDV {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
dataDate = try values.decode(String.self, forKey: .dataDate)
type = try values.decode(String.self, forKey: .type)
location = try values.decode(LocationDetails.self, forKey: .location)
}
}
extension LocationDetails {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
latitude = try values.decode(String.self, forKey: .latitude)
longitude = try values.decode(String.self, forKey: .longitude)
name = try values.decode(String.self, forKey: .name)
country = try values.decode(String.self, forKey: .country)
continent = try values.decode(String.self, forKey: .continent)
elevation = try values.decode(String.self, forKey: .elevation)
period = try [values.decode(Period.self, forKey: .period)]
}
}
extension Period {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
type = try values.decode(String.self, forKey: .type)
value = try values.decode(String.self, forKey: .value)
rep = try [values.decode(Rep.self, forKey: .rep)]
}
}
extension Rep {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
windDirection = try values.decode(String.self, forKey: .windDirection)
feelsLikeTemperature = try values.decode(String.self, forKey: .feelsLikeTemperature)
windGust = try values.decode(String.self, forKey: .windGust)
humidity = try values.decode(String.self, forKey: .humidity)
precipitation = try values.decode(String.self, forKey: .precipitation)
windSpeed = try values.decode(String.self, forKey: .windSpeed)
temperature = try values.decode(String.self, forKey: .temperature)
visibility = try values.decode(String.self, forKey: .visibility)
weatherType = try values.decode(String.self, forKey: .weatherType)
uvIndex = try values.decode(String.self, forKey: .uvIndex)
time = try values.decode(String.self, forKey: .time)
}
}
The data that I am trying to parse is:
{"SiteRep":{"Wx":{"Param":[{"name":"F","units":"C","$":"Feels Like Temperature"},{"name":"G","units":"mph","$":"Wind Gust"},{"name":"H","units":"%","$":"Screen Relative Humidity"},{"name":"T","units":"C","$":"Temperature"},{"name":"V","units":"","$":"Visibility"},{"name":"D","units":"compass","$":"Wind Direction"},{"name":"S","units":"mph","$":"Wind Speed"},{"name":"U","units":"","$":"Max UV Index"},{"name":"W","units":"","$":"Weather Type"},{"name":"Pp","units":"%","$":"Precipitation Probability"}]},"DV":{"dataDate":"2018-03-16T19:00:00Z","type":"Forecast","Location":{"i":"3844","lat":"50.7366","lon":"-3.40458","name":"EXETER AIRPORT 2","country":"ENGLAND","continent":"EUROPE","elevation":"27.0","Period":[{"type":"Day","value":"2018-03-16Z","Rep":[{"D":"SE","F":"8","G":"16","H":"78","Pp":"6","S":"11","T":"11","V":"EX","W":"7","U":"1","$":"900"},{"D":"SE","F":"6","G":"11","H":"88","Pp":"6","S":"9","T":"8","V":"MO","W":"7","U":"1","$":"1080"},{"D":"E","F":"5","G":"13","H":"92","Pp":"5","S":"4","T":"7","V":"GO","W":"7","U":"0","$":"1260"}]},{"type":"Day","value":"2018-03-17Z","Rep":[{"D":"E","F":"5","G":"16","H":"90","Pp":"86","S":"7","T":"7","V":"GO","W":"12","U":"0","$":"0"},{"D":"ENE","F":"5","G":"13","H":"93","Pp":"82","S":"7","T":"7","V":"GO","W":"15","U":"0","$":"180"},{"D":"ENE","F":"2","G":"22","H":"91","Pp":"40","S":"11","T":"6","V":"MO","W":"9","U":"0","$":"360"},{"D":"NE","F":"-2","G":"29","H":"84","Pp":"44","S":"16","T":"3","V":"VG","W":"12","U":"1","$":"540"},{"D":"ENE","F":"-4","G":"29","H":"75","Pp":"17","S":"16","T":"2","V":"VG","W":"8","U":"2","$":"720"},{"D":"ENE","F":"-4","G":"29","H":"72","Pp":"20","S":"16","T":"2","V":"VG","W":"8","U":"1","$":"900"},{"D":"NE","F":"-6","G":"25","H":"73","Pp":"17","S":"13","T":"0","V":"VG","W":"8","U":"1","$":"1080"},{"D":"NE","F":"-7","G":"22","H":"81","Pp":"16","S":"11","T":"-1","V":"VG","W":"8","U":"0","$":"1260"}]},{"type":"Day","value":"2018-03-18Z","Rep":[{"D":"NE","F":"-8","G":"22","H":"86","Pp":"51","S":"11","T":"-2","V":"VG","W":"24","U":"0","$":"0"},{"D":"NE","F":"-8","G":"22","H":"87","Pp":"60","S":"11","T":"-2","V":"GO","W":"24","U":"0","$":"180"},{"D":"NE","F":"-8","G":"25","H":"88","Pp":"66","S":"13","T":"-1","V":"MO","W":"24","U":"0","$":"360"},{"D":"ENE","F":"-8","G":"29","H":"92","Pp":"84","S":"16","T":"-1","V":"PO","W":"27","U":"1","$":"540"},{"D":"ENE","F":"-5","G":"31","H":"84","Pp":"63","S":"16","T":"1","V":"MO","W":"24","U":"2","$":"720"},{"D":"ENE","F":"-5","G":"29","H":"83","Pp":"26","S":"16","T":"1","V":"MO","W":"8","U":"1","$":"900"},{"D":"ENE","F":"-6","G":"25","H":"80","Pp":"24","S":"13","T":"0","V":"GO","W":"8","U":"1","$":"1080"},{"D":"ENE","F":"-7","G":"25","H":"78","Pp":"18","S":"13","T":"-1","V":"GO","W":"8","U":"0","$":"1260"}]},{"type":"Day","value":"2018-03-19Z","Rep":[{"D":"NE","F":"-8","G":"25","H":"78","Pp":"12","S":"11","T":"-2","V":"VG","W":"7","U":"0","$":"0"},{"D":"NE","F":"-8","G":"25","H":"78","Pp":"10","S":"13","T":"-2","V":"VG","W":"7","U":"0","$":"180"},{"D":"NE","F":"-8","G":"22","H":"77","Pp":"11","S":"11","T":"-2","V":"VG","W":"7","U":"0","$":"360"},{"D":"NE","F":"-7","G":"27","H":"69","Pp":"3","S":"13","T":"0","V":"VG","W":"3","U":"1","$":"540"},{"D":"ENE","F":"-3","G":"29","H":"57","Pp":"2","S":"16","T":"3","V":"VG","W":"3","U":"3","$":"720"},{"D":"NE","F":"0","G":"29","H":"49","Pp":"1","S":"16","T":"5","V":"VG","W":"1","U":"1","$":"900"},{"D":"NE","F":"-1","G":"20","H":"59","Pp":"1","S":"11","T":"4","V":"VG","W":"1","U":"1","$":"1080"},{"D":"NNE","F":"-4","G":"22","H":"73","Pp":"1","S":"11","T":"2","V":"VG","W":"0","U":"0","$":"1260"}]},{"type":"Day","value":"2018-03-20Z","Rep":[{"D":"NNE","F":"-4","G":"18","H":"81","Pp":"5","S":"9","T":"1","V":"VG","W":"7","U":"0","$":"0"},{"D":"N","F":"-3","G":"18","H":"86","Pp":"5","S":"9","T":"2","V":"GO","W":"7","U":"0","$":"180"},{"D":"N","F":"-3","G":"18","H":"88","Pp":"5","S":"9","T":"2","V":"GO","W":"7","U":"0","$":"360"},{"D":"N","F":"0","G":"20","H":"78","Pp":"5","S":"9","T":"4","V":"VG","W":"7","U":"1","$":"540"},{"D":"NNE","F":"3","G":"22","H":"68","Pp":"1","S":"11","T":"7","V":"VG","W":"3","U":"3","$":"720"},{"D":"N","F":"5","G":"22","H":"62","Pp":"5","S":"11","T":"8","V":"VG","W":"7","U":"1","$":"900"},{"D":"NNW","F":"3","G":"13","H":"72","Pp":"5","S":"7","T":"6","V":"VG","W":"7","U":"1","$":"1080"},{"D":"NNW","F":"1","G":"11","H":"82","Pp":"5","S":"4","T":"4","V":"GO","W":"7","U":"0","$":"1260"}]}]}}}}
However when I decode the JSON to the structure I get the error:
Error Serializing Json: keyNotFound(Clothing_Prediction_iOS_Application.Param.(CodingKeys in _99F6E563F35EF627A75B06F8891FEB0F).headings, Swift.DecodingError.Context(codingPath: [Clothing_Prediction_iOS_Application.WeatherRoot.(CodingKeys in _99F6E563F35EF627A75B06F8891FEB0F).siteRep, Clothing_Prediction_iOS_Application.SiteRep.(CodingKeys in _99F6E563F35EF627A75B06F8891FEB0F).dataWx, Clothing_Prediction_iOS_Application.DataWx.(CodingKeys in _99F6E563F35EF627A75B06F8891FEB0F).param, Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0))], debugDescription: "No value associated with key headings (\"Param\").", underlyingError: nil))
I have tried looking at optionals however I can not see why I am getting this error.
It looks like you're duplicating Param in your data structures. DataWx has case param = "Param", which is OK. But then Param has case headings = "Param", which is not in your JSON. So your JSON starts off with
{"SiteRep":{"Wx":{"Param":[{"name"...
But your data structures expect something like
{"SiteRep":{"Wx":{"Param":{"Param":...
The error isn't very clear but this seems to be what it's trying to tell you.
If you parse through the error message (messy, I know), you will find that it's telling you 2 things:
Key not found: headings
At key path: siteRep/dataWx/param[0]
The key path is the name of the properties in your data model. If you convert them back to how you mapped it in your various CodingKeyss, you will get the JSON path: SiteRep/Wx/Param[0]. There's no headings to found there.
How to fix it:
Remove your current Param struct
Rename WeatherDataHeadings to Param
You also have another mapping error in DataDV:
struct DataDV: Codable {
// Better parse date as Date, and not as String. This
// requires set the dateDecodingStrategy. See below.
var dataDate: Date
var type: String
var location: LocationDetails
private enum CodingKeys: String, CodingKey {
case dataDate = "dataDate" // not "dataType"
case type = "type"
case location = "Location"
}
}
And you wrote way more code than needed. You can delete all these extension. The compiler can synthesize them for you.
Here's how you decode it:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let weatherRoot = try decoder.decode(WeatherRoot.self, from: jsonData)