Reputation: 11
I am sorry if this question is answered many times but am looking for my own specific json issue that's why am posting this, This is my json its an array list of notifications and in the extra object you can see it has contract object, Now this object is changing it can be campaign or feedback in the 2nd index of the json array how do I make Codable struct for this to decode this type of jsonHere is my struct, I cannot do Any type in Codable
struct Alert: Decodable {
var id: Int?
var isUnread: Bool?
var userId: Int?
var message: String?
var notificationType: String?
var extra: Any?
"value": [
{
"id": 153,
"is_unread": true,
"user_id": 3,
"message": "Contract offered from JohnWick. Click here to see the details.",
"notification_type": "Contract",
"extra": {
"contract": {
"id": 477,
"likes": 0,
"shares": 0,
"account_reach": 0.0,
"followers": 0,
"impressions": 0.0,
"description": "Fsafasd",
"budget": 0.0,
"start_date": null,
"end_date": null,
"status": "pending",
"is_accepted": false,
"brand_id": 443,
"influencer_id": 3,
"proposal_id": 947,
"created_at": "2019-11-09T17:40:57.646Z",
"updated_at": "2019-11-09T17:40:57.646Z",
"contract_fee": 435345.0,
"base_fee": 5000.0,
"transaction_fee": 43534.5,
"total_fee": 483879.5,
"infuencer_completed": false,
"brand_completed": false,
"comments": 0
}
}
},
{
"id": 152,
"is_unread": true,
"user_id": 3,
"message": "Message from JohnWick. Click here to check your inbox.",
"notification_type": "Message",
"extra": {
"message": {
"id": 495,
"body": "Uuhvh",
"read": false,
"conversation_id": 42,
"user_id": 3,
"created_at": "2019-11-08T13:44:02.055Z",
"updated_at": "2019-11-08T13:44:02.055Z"
}
}
},
]
As you can see it can be message, or campaign , or contract or feedback so how do I parse this or make model for this with CodingKeys Codable
Upvotes: 0
Views: 511
Reputation: 285039
It's not Any
type, you got four different but predictable types.
With reference to Rob's answer you can get rid of the decoding attempts in the if - else if
chain if you decode notificationType
as enum and decode the subtypes according to its value
enum NotificationType : String, Decodable {
case contract = "Contract", message = "Message", campaign = "Campaign", feedback = "Feedback"
}
enum Extra {
case contract(Contract)
case campaign(Campaign)
case message(Message)
case feedback(Feedback)
}
struct Alert: Decodable {
let id: Int
let isUnread: Bool
let userId: Int
let message: String
let notificationType: NotificationType
let extra: Extra
private enum CodingKeys : String, CodingKey { case id, isUnread, userId, message, notificationType, extra}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
isUnread = try container.decode(Bool.self, forKey: .isUnread)
userId = try container.decode(Int.self, forKey: .userId)
message = try container.decode(String.self, forKey: .message)
notificationType = try container.decode(NotificationType.self, forKey: .notificationType)
switch notificationType {
case .contract:
let contractData = try container.decode([String:Contract].self, forKey: .extra)
extra = .contract(contractData[notificationType.rawValue.lowercased()]!)
case .campaign:
let campaignData = try container.decode([String:Campaign].self, forKey: .extra)
extra = .campaign(campaignData[notificationType.rawValue.lowercased()]!)
case .message:
let messageData = try container.decode([String:Message].self, forKey: .extra)
extra = .message(messageData[notificationType.rawValue.lowercased()]!)
case .feedback:
let feedbackData = try container.decode([String:Feedback].self, forKey: .extra)
extra = .feedback(feedbackData[notificationType.rawValue.lowercased()]!)
}
}
}
Upvotes: 0
Reputation: 1064
in this article, you can find a way that support any type with the codable, so it will not matter if JSON returns String or Int:
Upvotes: 1
Reputation: 299265
You don't mean Any
here. As you said, extra
can be "message, or campaign, or contract or feedback." It can't be a UIViewController or a CBPeripheral. It's not "anything." It's one of 4 things.
"One of X things" is an enum:
enum Extra {
case contract(Contract)
case campaign(Campaign)
case message(Message)
case feedback(Feedback)
}
To make it Decodable, we just need to look for the right key:
extension Extra: Decodable {
enum CodingKeys: CodingKey {
case contract, campaign, message, feedback
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Try to decode each thing it could be; throw if nothing matches.
if let contract = try? container.decode(Contract.self, forKey: .contract) {
self = .contract(contract)
} else if let campaign = try? container.decode(Campaign.self, forKey: .campaign) {
self = .campaign(campaign)
} else if let message = try? container.decode(Message.self, forKey: .message) {
self = .message(message)
} else if let feedback = try? container.decode(Feedback.self, forKey: .feedback) {
self = .feedback(feedback)
} else {
throw DecodingError.valueNotFound(Self.self,
DecodingError.Context(codingPath: container.codingPath,
debugDescription: "Could not find extra"))
}
}
}
Upvotes: 1
Reputation: 28539
A simple solution would be to make a struct called Extra
that has four optional properties for each of the cases that you have.
struct Extra: Codable {
let contract: Contract?
let message: Message?
let feedback: Feedback?
let campaign: Campaign?
}
As long as each of message, campaign, contract, and feedback have fixed responses then you should be able to make structs for them that conform to Codable
.
Upvotes: 1