Zhao_Xin
(Zhao Xin)
1
I have a json file like this.
{
"id": "1199786642468413448",
"voting_status": "closed",
"duration_minutes": 1440,
"options": [
{
"position": 1,
"label": "“C Sharp”",
"votes": 795
},
{
"position": 2,
"label": "“C Hashtag”",
"votes": 156
}
],
"end_datetime": "2019-11-28T20:26:41.000Z"
}
In the options array, the Dictionary Type is String:Any, Any is String or Int, So I cannot use Decodable here?
My code
public struct Poll:Decodable {
public let id:String
public let options:[[String:Any]]
public let duration_minutes:Int?
public let end_datetime:Date?
public let voting_status:String?
enum CodingKeys: String, CodingKey {
case id
case options
case duration_minutes
case end_datetime
case voting_status
}
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
do {
options = try values.decode([[String:Int]].self, forKey: .options)
} catch DecodingError.typeMismatch {
options = try values.decode([[String:String]].self, forKey: .options)
}
duration_minutes = try? values.decode(Int.self, forKey: .duration_minutes)
end_datetime = try? values.decode(Date.self, forKey: .end_datetime)
voting_status = try? values.decode(String.self, forKey: .voting_status)
}
}
My code won't work. Any Suggestions?
xAlien95
(Stefano De Carolis)
2
You don't need to define a custom init(from:) in your case. end_datetime isn't quite in ISO8601 format, so you need to provide the format to the JSONDecoder instance, but that can be handled outside of the Poll struct. The rest of the properties can already be handled by the Decodable conformances provided by default in the Swift standard library.
import Foundation
struct Poll: Decodable {
let id: String
let votingStatus: VotingStatus
let durationMinutes: Int
let options: [Option]
let endDateTime: Date
enum VotingStatus: String, Decodable {
case open, closed
}
struct Option: Decodable {
let position: Int
let label: String
let votes: Int
}
enum CodingKeys: String, CodingKey {
case id, options
case votingStatus = "voting_status"
case durationMinutes = "duration_minutes"
case endDateTime = "end_datetime"
}
}
- You could use an enumeration (
VotingStatus) to model voting_status, since it can presumably have a finite amount of values ("open", "closed", etc.).
- You could use a struct (
Option) to model options instead of falling back to a dictionary.
- You could provide more Swifty~ properties to the
Poll struct by using CodingKeys: for example via case votingStatus = "vating_status" you're able to place the decoded contents of the JSON voting_status field in the Swift votingStatus property.
let json = """
{
"id": "1199786642468413448",
"voting_status": "closed",
"duration_minutes": 1440,
"options": [{
"position": 1,
"label": "“C Sharp”",
"votes": 795
},{
"position": 2,
"label": "“C Hashtag”",
"votes": 156
}],
"end_datetime": "2019-11-28T20:26:41.000Z"
}
"""
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
decoder.dateDecodingStrategy = .formatted(formatter)
if
let data = json.data(using: .utf8),
let poll = try? decoder.decode(Poll.self, from: data)
{
dump(poll)
}
2 Likes
young
(rtSwift)
3
Why
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
...
doesn't work? I don't understand, it seems it should work. 
xAlien95
(Stefano De Carolis)
4
I guess that's due to "end_datetime", which I named endDateTime in the struct instead of endDatetime. If you name it endDatetime you can remove the CodingKeys enum and use .convertFromSnakeCase.
1 Like