2

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?

1
  • 1
    Create another enum for the error. e.g. errorResponseKeys which contains a message. When you get the nestedContainer use errorResponseKeys.self instead of loginResponseKeys.self Commented May 31, 2018 at 9:15

3 Answers 3

4

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"
         }
} 
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! I have to wrap my head around decoder and encoders. Thanks
You Are welcome , in cod-able any Dictionary in Json {} convert to struct , just think like that at first stage
Thanks. Good starting ground.
3

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)
        }
    }
}

Comments

-1

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)
    }
}

4 Comments

Take a look at @AbdelahadDarwish answer. No need for a third party library, no need for new confusing operators, and less code. Just because you've got a library to push, it does't mean it's the right answer to every Codable question.
@Ashley Mills but is a different approach and I think is good to show alternatives. In my solution you have single message property. Of course you can do it manually without any dependency
Be honest - you're just trying to promote your library. 5/6 of your answers are referring to it.
Not exactly. I tried to answer in way it will show how manual implementation of Codable can be simplified using the library. Of course you may not like it, you can use standard manual implementation of Codable, it's your choice. Until swift4 people were using external libraries like ObjectMapper or JSONModel but the others were using pure Dictionaries. In my opinion this proposition is really easy to write and understand and what is the most important it will not force you to use it on every model you have in the code. But also your vote is response for me that I really respect and thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.