I have an address type with a property address1 that I'm populating from an API. The API uses snake case and names this property address_1. I've used CodingKeys to specify the key name, and it creates JSON with the specified key name but doesn't read it.
Is there any way to get a snake case conversion from address_1 to work? How can I tell what key Swift's Decoder is looking for?
import Foundation
struct Address : Codable {
var address1 : String
var city : String
var state : String
var postalCode : String
enum CodingKeys: String, CodingKey {
case address1 = "address_1"
case city, state, postalCode
}
}
var addr = Address(address1: "123 Anywhere Ln", city: "Vero Beach", state: "FL", postalCode: "32968")
let encoder1 = JSONEncoder()
encoder1.keyEncodingStrategy = .convertToSnakeCase
let data1 = try encoder1.encode(addr)
print(String(data: data1, encoding: .utf8)!)
=> {"address_1":"123 Anywhere Ln","state":"FL","city":"Vero Beach","postal_code":"32968"}
let encoder2 = JSONEncoder()
let data2 = try encoder2.encode(addr)
print(String(data: data2, encoding: .utf8)!)
=> {"address_1":"123 Anywhere Ln","state":"FL","city":"Vero Beach","postalCode":"32968"}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
var addr2 = try decoder.decode(Address.self, from: data1)
print(addr2)
=> Fatal error: Error raised at top level: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "address_1", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"address_1\", intValue: nil) (\"address_1\").", underlyingError: nil)): file /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.8.25.8/swift/stdlib/public/core/ErrorType.swift, line 200
I think you shot yourself in the foot there. By specifying the actual key in CodingKeys you eliminated the need for the .convertFromSnakeCase decoding strategy.
I don't know for sure, but my guess is that the strategy causes the incoming JSON key "address_1" to first be converted to "address1", at which point it no longer matches the key you said you're looking for, i.e. "address_1". Hence, the error that no value is associated with key "address_1".
In the other direction, converting "address_1"to snake case does nothing, so it seems to work correctly.
i.e., upon encountering a snake_case key in the payload, looks up the camelCase equivalent in the CodingKeys enum. Because you're assigning a snake_case value for the address1 key already, no camelCase key can be found.
In this case, you can either continue using .convertToSnakeCase/.convertFromSnakeCase and dropping the custom key for address1 (in this case, it means you could drop the whole CodingKeys enum entirely since you're not customizing any other keys), or assigning snake_case keys to all of the properties you care about and dropping the strategy.
You use keyEncodingStrategy or keyDecodingStrategy on encoding, decoding or both when you want to automatically apply that conversion to all CodingKeys. You may want to apply per item encoding or decoding strategy then you make CodingKeys conform to String and assign for each custom element the appropriate encoding or decoding name, in that case you don't have to use any global keyEncodingStrategy or keyDecodingStrategy.