Reputation: 1399
I am trying to decode a nested JSON. The problem is that the top level and a nested keys' names are similar. Like:
{
success: bool
message: String
error: {
message: String
}
}
From the back end I would be receiving a success message or a failed message. If the success is true, the error key would not be returned back and if it is false, then the error along with the message is sent.
so if it is successful:
{
success: true
message: "Success message"
}
If it fails:
{
success: false
error:{
message: "Failed message"
}
}
The above will be the returned json. This is my struct for decoding:
struct loginResponse : Codable{
var success: Bool
var success_message: String
var error_message: String
enum loginResponseKeys: String, CodingKey{
case success
case error
case success_message = "message" // raw value is not unique
case error_message = "message"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: loginResponseKeys.self)
let error = try container.nestedContainer(keyedBy: loginResponseKeys.self, forKey: .error)
error_message = try error.decode(String.self, forKey: .error_message)
let message = try container.decode(String.self, forKey:.success_message)
}
Rightly so, it says that the raw value is not unique. But how do I overcome that?
Upvotes: 2
Views: 652
Reputation: 61
Using KeyedCodable it may be, notice you will have single "message" property that will contain successful or failed message depending on "success" value:
struct LoginResponse: Codable, Keyedable {
private(set) var success: Bool!
private(set) var message: String!
mutating func map(map: KeyMap) throws {
try success <-> map["success"]
let messageKey = success ? "message" : "error.message"
try message <-> map[messageKey]
}
init(from decoder: Decoder) throws {
try KeyedDecoder(with: decoder).decode(to: &self)
}
}
Upvotes: -1
Reputation: 6067
you can create struct for ErrorMessage
struct LoginResponse: Codable {
let success: Bool
let message: String?
let error: ErrorMessage?
}
struct ErrorMessage: Codable {
let message: String?
}
extension LoginResponse {
init(data: Data) throws {
self = try JSONDecoder().decode(LoginResponse.self, from: data)
}
}
Assume that this Json:
{
"success": true,
"message": "success",
"error": {
"message": "Error Message"
}
}
Upvotes: 4
Reputation: 54706
You just need to create a nested ErrorResponse
struct. Make both message
and error
optional and only decode one of them depending on the value of success
.
You should also conform to the Swift naming convention, which is UpperCamelCase for type names and lowerCamelCase for variable names.
struct LoginResponse : Codable{
let success: Bool
var successMessage: String?
var error: ErrorResponse?
struct ErrorResponse: Codable {
let message: String
}
enum LoginResponseKeys: String, CodingKey{
case success, error, successMessage = "message"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: LoginResponseKeys.self)
success = try container.decode(Bool.self, forKey: .success)
if success {
successMessage = try container.decode(String.self, forKey: .successMessage)
} else {
error = try container.decode(ErrorResponse.self, forKey: .error)
}
}
}
Upvotes: 3