2

I am trying to decode the error as follows, most of the error that I am handling in array format [String], but in few cases the error is not in array format, just a String.

If error comes in array format name comes as errors, but if it is string format then it comes as error. How could I handle this scenario?

How could I able to handle this scenario?

struct CustomError: Codable {
  let errors: [String]
}

private func errorDecoding(data : Data) {
 let decoder = JSONDecoder()
 do {
  let errorData = try decoder.decode(CustomError.self, from: data)
 } catch {
  // TODO
 }
}
gcharita
  • 7,729
  • 3
  • 20
  • 37
casillas
  • 16,351
  • 19
  • 115
  • 215

1 Answers1

5

You'd have to manually implement init(from:) and try decoding one type, failing that, decode another:

struct CustomError {
  let errors: [String]
}

extension CustomError: Decodable {
  enum CodingKeys: CodingKey { case errors, error }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    do {
       self.errors = try container.decode([String].self, forKey: .errors) 
    } catch DecodingError.typeMismatch, 
            DecodingError.keyNotFound {
       let error = try container.decode(String.self, forKey: .error)
       self.errors = [error]
    }
  }
}

The decoding part is normal:

do {
   let error = try JSONDecoder().decode(CustomError.self, from: data)
} catch {
  // ..
}

New Dev
  • 48,427
  • 12
  • 87
  • 129
  • Thanks for quick answer. If error comes in array format name comes as `errors`, but if it is string format then it comes as `error`. How could I handle this scenario? – casillas Oct 11 '20 at 17:38
  • I would like to catch the error and throw it if there is any issues, I think do and catch is needed? – casillas Oct 11 '20 at 17:42
  • Yeah, the `JSONDecoder().decode` part can be left unchanged... because it could, in principle, throw if the JSON is malformed – New Dev Oct 11 '20 at 17:44
  • Is it possible to add this missing part in your code, then I will mark it as an answer? – casillas Oct 11 '20 at 17:45
  • 2
    @NewDev `init(from:)` function just needs a `throws` at the end to pass the compile. – gcharita Oct 11 '20 at 17:46
  • 1
    You are throwing away the error unnecessarily. There is no need to use `try?`. Just catch the typeMismatch DecodingError and decode your string `do {` `self.errors = try container.decode([String].self, forKey: .errors)` `} catch DecodingError.typeMismatch {` `self.errors = try [container.decode(String.self, forKey: .error)]` `}` – Leo Dabus Oct 11 '20 at 19:03
  • 1
    @LeoDabus, i think you'd also need to catch `keyNotFound`, but yeah - I can update the answer – New Dev Oct 11 '20 at 19:25
  • @NewDev I didn't even realized you had different keys for the same property. No need to use `CodingKeyes` at all. Just use `SingleValueDecodingContainer`. => `let container = try decoder.singleValueContainer()` `do {` `self.errors = try container.decode([String].self)` `} catch DecodingError.typeMismatch {` `self.errors = try [container.decode(String.self)]` `}` – Leo Dabus Oct 11 '20 at 21:14
  • @LeoDabus, how though? I guess the OP wasn't clear, but I assumed a JSON of `{ "errors": ["A", "B"] }` or alternatively `{ "error": "A" }`. This would fail with a type mismatch: `"Expected to decode String but found a dictionary instead."` if I used a single value container – New Dev Oct 15 '20 at 00:29
  • @NewDev yes. without the actual json we can't really know – Leo Dabus Oct 15 '20 at 00:31