Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift JSONDecoder - Coding Keys not working with underscore

Can someone please explain why code in part A works, and B does not please. It has me baffled.

WORKING

struct Coded : Codable, Hashable {  
  public let avar1: String
  public let avar2: String

  enum CodingKeys: String, CodingKey {
    case avar1 = "avar1"
    case avar2 = "avar2"
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    print (container.allKeys)
    avar1 = try container.decode(String.self, forKey: .avar1)
    avar2 = try container.decode(String.self, forKey: .avar2)
  }
}

let JSONStr = """
{
  "avar1": "This is a string",
  "avar2": "This is a string2",
}
"""

if let jsdata = JSONStr.data(using: .utf8) {
  let decoder = JSONDecoder()
  decoder.keyDecodingStrategy = .convertFromSnakeCase
  let aobj: Coded? = try? decoder.decode(Coded.self, from: jsdata)
  print (aobj ?? "No object")
}

OUTPUT

[CodingKeys(stringValue: "avar1", intValue: nil), CodingKeys(stringValue: "avar2", intValue: nil)]
Coded(avar1: "This is a string", avar2: "This is a string2")

NOT WORKING

struct Coded : Codable, Hashable {  
  public let avar1: String
  public let avar2: String

  enum CodingKeys: String, CodingKey {
    case avar1 = "avar1"
    case avar2 = "avar_2"
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    print (container.allKeys)
    avar1 = try container.decode(String.self, forKey: .avar1)
    avar2 = try container.decode(String.self, forKey: .avar2)
  }
}

let JSONStr = """
{
  "avar1": "This is a string",
  "avar_2": "This is a string2",
}
"""

if let jsdata = JSONStr.data(using: .utf8) {
  let decoder = JSONDecoder()
  decoder.keyDecodingStrategy = .convertFromSnakeCase
  let aobj: Coded? = try? decoder.decode(Coded.self, from: jsdata)
  print (aobj ?? "No object")
}

OUTPUT

[CodingKeys(stringValue: "avar1", intValue: nil)]
No object

The second function will only show the coding key without the underscore. But as soon as I remove the underscore it has the coding key in allKeys...

Swift 4.2 - Xcode 10.2.

Any ideas?

like image 826
Peter Suwara Avatar asked Sep 20 '25 10:09

Peter Suwara


1 Answers

.convertFromSnakeCase converts snake_cased variables to camelCase before accessing the CodingKeys.

If you want to specify CodingKeys you have to use the converted value in your NOT WORKING example

enum CodingKeys: String, CodingKey {
    case avar1 = "avar1"
    case avar2 = "avar2"
}

But this illustrates the pointlessness of the CodingKeys. So think the other way round and take advantage of the key decoding strategy.

Rather than removing .convertFromSnakeCase remove the CodingKeys and the initializer.

And catch always possible Decoding errors.

struct Coded : Codable {
    public let avar1: String
    public let avar2: String
}

let jsonStr = """
{
"avar1": "This is a string",
"avar_2": "This is a string2",
}
"""

let jsdata = Data(jsonStr.utf8)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
    let aobj = try decoder.decode(Coded.self, from: jsdata)
    print(aobj)
} catch { print(error) }
like image 190
vadian Avatar answered Sep 23 '25 01:09

vadian