Struct Codable. Unable to Encode nested json - json

I have checked severa other questions asked on SO, even though they look similar they did not address my needs and challenges I currently have in an implementation. The questions and answer I have seen so far addresses more of Decodable. However I am currently having issue wit Encodable
I have nested JSON structure Like the below. I am able to successfully decoable and pass the json
"data": {
"id": "XXXXXXXXXX",
"tKey": "XXXXXXXX",
"tID": "XXXXX",
"type": "XXXXX",
"hasEvent": true,
"location": "XXXXXXXXXXXXX",
"cover": {
"name": "XXXXXXX",
"parentId": "XXXXXXX",
"parentName": "XXXXXX",
"pop": {
"logo": "*********",
}
},
"men": [
{
"id": "XXXXXXXXXXX",
"title": "XXXXXXX"
},
{
"id": "XXXXXX",
"title": "XXXXXX"
},
{
"id": "XXXXXX",
"title": "XXXXXX"
}
],
"homes": [],
"homeTypes": [{
"id": "XXXXXX",
"subMenuId": "XXXXXXXX",
"type": "Flat",
"data": {
"latitude": 29.767,
"longitude": 0,0000,
},
"datas": null,
"component": null
},
{
"id": "XXXXXXXXX",
"subMenuId": "XXXXX",
"type": "Bubgalow",
"data": {
"id": "XXXXXXXXX,
"title": "XXXXXXXXX",
"summary": "Hello;",
}
}
]
}
The decodable works fine as I am able to pass in response from the server and populate tableview. However on trying to encode using encodable protocol it does not seem to work. Please find the code snippet below that I have tried:
Struct DataObject: Codable {
var id: String = ""
var location: String = ""
var tKey: String = ""
var tId: String = ""
var cover: cover!
var homeTypes: [Type] = [TYpe]()
var flat = Flat()
var bungalow = Bungalow()
var flat = Flat()
var bungalow = Bungalow()
enum CodingKeys: String, CodingKey {
case id = "id"
case type = "type"
case location = "location"
case tKey = "tKey"
case tId = "tId"
case cover = "cover"
case homeTypes = "homeTypes"
}
enum ComponentCodingKeys: String, CodingKey {
case id = "id"
case type = "type"
case component = "component"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
if container.contains(.tKey) {
tKey = try container.decodeIfPresent(String.self, forKey: .tKey) ?? ""
}
if container.contains(.tId) {
tId = try container.decodeIfPresent(String.self, forKey: .tId) ?? ""
}
let typeRawValue = try container.decode(String.self, forKey: .type)
if let tempType = SomeType(rawValue: typeRawValue) {
type = tempType
}
if container.contains(.location) {
location = try container.decodeIfPresent(String.self, forKey: .location) ?? ""
}
cover = try container.decode(Cover.self, forKey: .cover)
homeTypes = try container.decode([HomeTypes].self, forKey: .component)
for homeType in homeTypes {
switch homeType.type {
case .flat:
flat = homeType.flat
}
case .bungalow:
bungalow = homeType.bungalow
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(location, forKey: .location)
try container.encode(tKey, forKey: .tKey)
try container.encode(tId, forKey: .tId)
try container.encode(cover, forKey: .cover)
//let rawValueType = try container.encode(type, forKey: .type)
var homes = container.nestedUnkeyedContainer(forKey: .component)
try homeTypes.forEach {
try homes.encode($0)
}
}
}
}

There are several issues with your code. Tidy these up first and then you might have a clearer idea what's going on
Use let rather than var. If a value doesn't exist, use nil rather than an empty string.
struct DataObject: Codable {
let id: String
let location: String?
let tKey: String?
let tId: String?
let cover: Cover
let homeTypes: [Type]
}
You don't need to give your CodingKeys a string value if they match the json keys. So just…
enum CodingKeys: CodingKey {
case id, type, location, tKey, tId, cover, homeTypes
}
No need to test if a key exists if you're using decodeIfPresent
tKey = try container.decodeIfPresent(String.self, forKey: .tKey)
Make Type: Codable, then
try container.encode(homeTypes, forKey: .homeTypes)

Related

Complex JSON in Swift. How to get the data correctly. Different structures

Faced a difficult problem for me, when receiving data I don’t know how to decompose data in one array.
The responsible variable contains different types of data.
Do I get it right? I think in the initializer to go through the possible options and substitute the desired one? What type should the variable of this array be?
[
{
"id": 42,
"created_at": "2021-09-08T08:55:58.000000Z",
"updated_at": "2021-09-08T08:55:58.000000Z",
"link": "u4986",
"type": "u",
"responsible": {
"id": 4986,
"type": "management_company",
"email": "X#X.com",
"phone": "+0000000000",
"comment": null,
"first_name": "Alex",
"second_name": "Hook"
}
},
{
"id": 43,
"created_at": "2021-09-08T08:55:58.000000Z",
"updated_at": "2021-09-08T08:55:58.000000Z",
"link": "r14",
"type": "r",
"responsible": {
"id": 14,
"name": "manager",
"guard_name": "api",
"created_at": "2021-06-15T19:20:20.000000Z",
"updated_at": "2021-06-15T19:20:20.000000Z"
}
}
]
How to make an initializer for MyJson
struct MyJson: Codable {
let id: Int
let createdAt: String
let updatedAt: String
let link: String
let type: String
let responsible: Any
}
// MARK: - Responsible
struct User: Codable {
let id: Int
let type, email, phone, comment: String
let firstName, secondName: String
}
struct UserCategory: Codable {
let id: Int
let name, guardName, createdAt, updatedAt: String
}
In swift JSON parsing is pretty straightforward, build an object that reflects your JSON (I've just built an example based on your JSON here):
struct JsonExample: Decodable {
let id: Int
let responsible: [Responsible]
struct Responsible: Decodable {
let id: Int
let email: String
let guard_name: String
}
}
and then just decode it
let jsonData = "json_string".data(using: .utf8)!
do {
let decoded = try JSONDecoder().decode([JsonExample].self, from: jsonData)
} catch let error {
print(error.localizedDescription)
}
If you want to distinguish between nested objects you can use init and use a property inside your JSON to get the job done
init(from decoder : Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
let type = try container.decode(String.self, forKey: .type)
if type == "a" {
let data = try container.decode([ResponsibleA].self, forKey: . responsible)
responsible = .responsibleA(data)
} else { // add better error handling
let data = try container.decode([ResponsibleB].self, forKey: . responsible)
responsible = .responsibleB(data)
}
}
Completed the task for a very long time. Finally I solved the problem. If you are facing this problem. You will find the solution here Indeterminate Types with Codable in Swift

Decoding JSON with Swift containing dictionary with different types

I have a JSON format that I'm trying to parse with JSONDecoder but due to the way the JSON is structured, I can't figure out how to do it.
Here's the format of the JSON. I'm going to leave out bits for brevity.
{
"name":"John Smith",
"addresses":[
{
"home":{
"street":"123 Main St",
"state":"CA"
}
},
{
"work":{
"street":"345 Oak St",
"state":"CA"
}
},
{
"favorites":[
{
"street":"456 Green St.",
"state":"CA"
},
{
"street":"987 Tambor Rd",
"state":"CA"
}
]
}
]
}
I don't know how to define a Decodable struct that I can then decode. addresses is an array of dictionaries. home and work each contain a single address while favorites contains an array of addresses. I can't define addresses as [Dictionary<String, Address] since favorites is an array of addresses. I can't define it as [Dictionary<String, Any>] because then I get an Type 'UserProfile' does not conform to protocol 'Encodeable' error.
Does anyone have any ideas how I can parse this? How do I parse a dictionary where the value changes depending on the key?
Thank you.
I assume that your JSON is something like this:
{
"name": "John Smith",
"addresses": [
{
"home": {
"street": "123 Main St",
"state": "CA"
}
},
{
"work": {
"street": "345 Oak St",
"state": "CA"
}
},
{
"favorites": [
{
"street": "456 Green St.",
"state": "CA"
},
{
"street": "987 Tambor Rd",
"state": "CA"
}
]
}
]
}
I had to make some changes to be a valid JSON.
You can use the following structure to always map addresses property to [[String: [Address]]]:
struct Response: Decodable {
let name: String
let addresses: [[String: [Address]]]
enum CodingKeys: String, CodingKey {
case name
case addresses
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
var unkeyedContainer = try container.nestedUnkeyedContainer(forKey: .addresses)
var addresses = [[String: [Address]]]()
while !unkeyedContainer.isAtEnd {
do {
let sindleAddress = try unkeyedContainer.decode([String: Address].self)
addresses.append(sindleAddress.mapValues { [$0] } )
} catch DecodingError.typeMismatch {
addresses.append(try unkeyedContainer.decode([String: [Address]].self))
}
}
self.addresses = addresses
}
}
struct Address: Decodable {
let street: String
let state: String
}
Basically, in the custom implementation of init(from:) we try to decode addresses property to [String: Address], if that succeeds a new dictionary of type [String: [Address]] is created with only one element in the value array. If it fails then we decode addresses property to [String: [Address]].
Update: I would prefer though to add another struct:
struct AddressType {
let label: String
let addressList: [Address]
}
and modify the addresses property of Response to be [AddressType]:
struct Response: Decodable {
let name: String
let addresses: [AddressType]
enum CodingKeys: String, CodingKey {
case name
case addresses
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
var unkeyedContainer = try container.nestedUnkeyedContainer(forKey: .addresses)
var addresses = [AddressType]()
while !unkeyedContainer.isAtEnd {
let addressTypes: [AddressType]
do {
addressTypes = try unkeyedContainer.decode([String: Address].self).map {
AddressType(label: $0.key, addressList: [$0.value])
}
} catch DecodingError.typeMismatch {
addressTypes = try unkeyedContainer.decode([String: [Address]].self).map {
AddressType(label: $0.key, addressList: $0.value)
}
}
addresses.append(contentsOf: addressTypes)
}
self.addresses = addresses
}
}
A possible solution, using an enum, which might be either a work, home or fav:
struct Top: Decodable {
let name: String
let addresses: [AddressType]
enum CodingKeys: String, CodingKey {
case name
case addresses
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.addresses = try container.decode([AddressType].self, forKey: .addresses)
}
}
enum AddressType: Decodable {
case home(Address)
case work(Address)
case favorites([Address])
enum CodingKeys: String, CodingKey {
case home
case work
case favorites
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let home = try container.decodeIfPresent(Address.self, forKey: .home) {
self = AddressType.home(home)
} else if let work = try container.decodeIfPresent(Address.self, forKey: .work) {
self = AddressType.work(work)
} else {
let favorites = try container.decodeIfPresent([Address].self, forKey: .favorites)
self = AddressType.favorites(favorites ?? [])
}
}
}
struct Address: Decodable {
let street: String
let state: String
}
Testing (with fixes on your JSON I guess):
let jsonStr = """
{
"name": "John Smith",
"addresses": [{
"home": {
"street": "123 Main St",
"state": "CA"
}
}, {
"work": {
"street": "345 Oak St",
"state": "CA"
}
}, {
"favorites": [{
"street": "456 Green St.",
"state": "CA"
}, {
"street": "987 Tambor Rd",
"state": "CA"
}]
}]
}
"""
let jsonData = jsonStr.data(using: .utf8)!
do {
let top = try JSONDecoder().decode(Top.self, from: jsonData)
print("Top.name: \(top.name)")
top.addresses.forEach {
switch $0 {
case .home(let address):
print("It's a home address:\n\t\(address)")
case .work(let address):
print("It's a work address:\n\t\(address)")
case .favorites(let addresses):
print("It's a favorites addresses:")
addresses.forEach{ aSubAddress in
print("\t\(aSubAddress)")
}
}
}
} catch {
print("Error: \(error)")
}
Output:
$>Top.name: John Smith
$>It's a home address:
Address(street: "123 Main St", state: "CA")
$>It's a work address:
Address(street: "345 Oak St", state: "CA")
$>It's a favorites addresses:
Address(street: "456 Green St.", state: "CA")
Address(street: "987 Tambor Rd", state: "CA")
Note:
Afterwards, you should be able to have lazy variables on Top depending on your needs:
lazy var homeAddress: Address? = {
return self.addresses.compactMap {
if case AddressType.home(let address) = $0 {
return address
}
return nil
}.first
}()

parsing json any type with Codable

I am sorry if this question is answered many times but am looking for my own specific json issue that's why am posting this, This is my json its an array list of notifications and in the extra object you can see it has contract object, Now this object is changing it can be campaign or feedback in the 2nd index of the json array how do I make Codable struct for this to decode this type of jsonHere is my struct, I cannot do Any type in Codable
struct Alert: Decodable {
var id: Int?
var isUnread: Bool?
var userId: Int?
var message: String?
var notificationType: String?
var extra: Any?
"value": [
{
"id": 153,
"is_unread": true,
"user_id": 3,
"message": "Contract offered from JohnWick. Click here to see the details.",
"notification_type": "Contract",
"extra": {
"contract": {
"id": 477,
"likes": 0,
"shares": 0,
"account_reach": 0.0,
"followers": 0,
"impressions": 0.0,
"description": "Fsafasd",
"budget": 0.0,
"start_date": null,
"end_date": null,
"status": "pending",
"is_accepted": false,
"brand_id": 443,
"influencer_id": 3,
"proposal_id": 947,
"created_at": "2019-11-09T17:40:57.646Z",
"updated_at": "2019-11-09T17:40:57.646Z",
"contract_fee": 435345.0,
"base_fee": 5000.0,
"transaction_fee": 43534.5,
"total_fee": 483879.5,
"infuencer_completed": false,
"brand_completed": false,
"comments": 0
}
}
},
{
"id": 152,
"is_unread": true,
"user_id": 3,
"message": "Message from JohnWick. Click here to check your inbox.",
"notification_type": "Message",
"extra": {
"message": {
"id": 495,
"body": "Uuhvh",
"read": false,
"conversation_id": 42,
"user_id": 3,
"created_at": "2019-11-08T13:44:02.055Z",
"updated_at": "2019-11-08T13:44:02.055Z"
}
}
},
]
As you can see it can be message, or campaign , or contract or feedback so how do I parse this or make model for this with CodingKeys Codable
A simple solution would be to make a struct called Extra that has four optional properties for each of the cases that you have.
struct Extra: Codable {
let contract: Contract?
let message: Message?
let feedback: Feedback?
let campaign: Campaign?
}
As long as each of message, campaign, contract, and feedback have fixed responses then you should be able to make structs for them that conform to Codable.
You don't mean Any here. As you said, extra can be "message, or campaign, or contract or feedback." It can't be a UIViewController or a CBPeripheral. It's not "anything." It's one of 4 things.
"One of X things" is an enum:
enum Extra {
case contract(Contract)
case campaign(Campaign)
case message(Message)
case feedback(Feedback)
}
To make it Decodable, we just need to look for the right key:
extension Extra: Decodable {
enum CodingKeys: CodingKey {
case contract, campaign, message, feedback
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Try to decode each thing it could be; throw if nothing matches.
if let contract = try? container.decode(Contract.self, forKey: .contract) {
self = .contract(contract)
} else if let campaign = try? container.decode(Campaign.self, forKey: .campaign) {
self = .campaign(campaign)
} else if let message = try? container.decode(Message.self, forKey: .message) {
self = .message(message)
} else if let feedback = try? container.decode(Feedback.self, forKey: .feedback) {
self = .feedback(feedback)
} else {
throw DecodingError.valueNotFound(Self.self,
DecodingError.Context(codingPath: container.codingPath,
debugDescription: "Could not find extra"))
}
}
}
in this article, you can find a way that support any type with the codable, so it will not matter if JSON returns String or Int:
anycodableValue
It's not Any type, you got four different but predictable types.
With reference to Rob's answer you can get rid of the decoding attempts in the if - else if chain if you decode notificationType as enum and decode the subtypes according to its value
enum NotificationType : String, Decodable {
case contract = "Contract", message = "Message", campaign = "Campaign", feedback = "Feedback"
}
enum Extra {
case contract(Contract)
case campaign(Campaign)
case message(Message)
case feedback(Feedback)
}
struct Alert: Decodable {
let id: Int
let isUnread: Bool
let userId: Int
let message: String
let notificationType: NotificationType
let extra: Extra
private enum CodingKeys : String, CodingKey { case id, isUnread, userId, message, notificationType, extra}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
isUnread = try container.decode(Bool.self, forKey: .isUnread)
userId = try container.decode(Int.self, forKey: .userId)
message = try container.decode(String.self, forKey: .message)
notificationType = try container.decode(NotificationType.self, forKey: .notificationType)
switch notificationType {
case .contract:
let contractData = try container.decode([String:Contract].self, forKey: .extra)
extra = .contract(contractData[notificationType.rawValue.lowercased()]!)
case .campaign:
let campaignData = try container.decode([String:Campaign].self, forKey: .extra)
extra = .campaign(campaignData[notificationType.rawValue.lowercased()]!)
case .message:
let messageData = try container.decode([String:Message].self, forKey: .extra)
extra = .message(messageData[notificationType.rawValue.lowercased()]!)
case .feedback:
let feedbackData = try container.decode([String:Feedback].self, forKey: .extra)
extra = .feedback(feedbackData[notificationType.rawValue.lowercased()]!)
}
}
}

Swift 4 decode complex nested JSON

I'm trying to create a cleaner codable structure in which I can access the "description" by just typing day.description rather than day.weather.description
The description value is nested in an array "weather" which only contains a single object. I would like to extract the description from the index 0 and assign it to description in my struct.
Here is the JSON that I'm working with:
{
"dt": 1558321200,
"main": {
"temp": 11.88,
"temp_min": 11.88,
"temp_max": 11.88,
"pressure": 1013.3,
"sea_level": 1013.3,
"grnd_level": 1003.36,
"humidity": 77,
"temp_kf": 0
},
"weather": [{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01n"
}],
"clouds": {
"all": 0
},
"wind": {
"speed": 5.58,
"deg": 275.601
},
"sys": {
"pod": "n"
},
"dt_txt": "2019-05-20 03:00:00"
}
and the code I have so far:
struct Weather: Codable {
let days: [Day]
enum CodingKeys: String, CodingKey {
case days = "list"
}
}
struct Day: Codable {
let date: String
let main: Main
let wind: Wind
let description: String
enum CodingKeys: String, CodingKey {
case date = "dt_txt"
case main
case wind
case weather
case description
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
date = try container.decode(String.self, forKey: .date)
main = try container.decode(Main.self, forKey: .main)
wind = try container.decode(Wind.self, forKey: .wind)
let weather = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .weather)
description = try weather.decode(String.self, forKey: .description)
}
}
As you've been told, the simplest approach is just a computed property that references the desired value. However, for the sake of completeness we might as well discuss how to do the thing you actually asked to do. Let's illustrate with a reduced version of your JSON:
{
"dt": 1558321200,
"weather": [{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01n"
}]
}
So the question is, how can we parse this into, say, a struct Result with a description property such that we fetch out the "description" key from the first item in the "weather" array? Here's one approach:
struct Result : Decodable {
let description : String
enum Keys : CodingKey {
case weather
}
struct Weather : Decodable {
let description : String
}
init(from decoder: Decoder) throws {
let con = try! decoder.container(keyedBy: Keys.self)
var arr = try! con.nestedUnkeyedContainer(forKey: .weather) // weather array
let oneWeather = try! arr.decode(Weather.self) // decode first element
self.description = oneWeather.description
}
}
Basically the idea here is that nestedUnkeyedContainer gives us our array, and subsequent calls to decode on that array automatically tackle each element in turn. We have only one element so we need only one decode call. How we dispose of the resulting string is up to us, so now we can slot it into our top-level description property.
But here's another approach. We don't even really need the secondary Weather struct; we can just dive right into the "weather" array and grab the first dictionary element and access its "description" key without saying any more about what's in that inner dictionary, like this:
struct Result : Decodable {
let description : String
enum Keys : CodingKey {
case weather
case description
}
init(from decoder: Decoder) throws {
let con = try! decoder.container(keyedBy: Keys.self)
var arr = try! con.nestedUnkeyedContainer(forKey: .weather)
let con2 = try! arr.nestedContainer(keyedBy: Keys.self)
let desc = try! con2.decode(String.self, forKey: .description)
self.description = desc
}
}
Your question was not very complete (you didn't show your real JSON, just an excerpt), so I can't give any more precise advice, but I'm sure you can see how adapt this technique to your needs.

How to decode variable from json when key is changing according to user input?

I am trying to parse some JSON response coming from CoinmarketCap using the JSONDecoder() in Swift 4. But the problem is that the response from json is changing according to user input. e.g if user wants the price in eur, the output is following:
[
{
"price_eur": "9022.9695444"
}
]
but if user wants the price in gbp:
[
{
"price_gbp": "7906.8032145"
}
]
So the question is how should I make the struct that inherits from Decodable if the variable(json key) name is changing?
You can decode the dynamic key by creating a custom init(from:) method for your struct, then using two set of coding keys, an enum containing all keys that are known at compile time and another struct that you initialize using the dynamic keys that are generated using user input (contain the name of the currency).
In your custom init(from:) method you just need to decode each property using their respective keys.
let chosenCurrency = "gbp"
struct CurrencyResponse: Decodable {
let name:String
let symbol:String
let price:String
private static var priceKey:String {
return "price_\(chosenCurrency)"
}
private enum SimpleCodingKeys: String, CodingKey {
case name, symbol
}
private struct PriceCodingKey : CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
return nil
}
}
init(from decoder:Decoder) throws {
let values = try decoder.container(keyedBy: SimpleCodingKeys.self)
name = try values.decode(String.self, forKey: .name)
symbol = try values.decode(String.self, forKey: .symbol)
let priceValue = try decoder.container(keyedBy: PriceCodingKey.self)
price = try priceValue.decode(String.self, forKey: PriceCodingKey(stringValue:CurrencyResponse.priceKey)!)
}
}
do {
let cryptoCurrencies = try JSONDecoder().decode([CurrencyResponse].self, from: priceJSON.data(using: .utf8)!)
} catch {
print(error)
}
Test JSON:
let priceJSON = """
[
{
"id": "bitcoin",
"name": "Bitcoin",
"symbol": "BTC",
"rank": "1",
"price_\(chosenCurrency)": "573.137",
"price_btc": "1.0",
"24h_volume_\(chosenCurrency)": "72855700.0",
"market_cap_\(chosenCurrency)": "9080883500.0",
"available_supply": "15844176.0",
"total_supply": "15844176.0",
"percent_change_1h": "0.04",
"percent_change_24h": "-0.3",
"percent_change_7d": "-0.57",
"last_updated": "1472762067"
},
{
"id": "ethereum",
"name": "Ethereum",
"symbol": "ETH",
"rank": "2",
"price_\(chosenCurrency)": "12.1844",
"price_btc": "0.021262",
"24h_volume_\(chosenCurrency)": "24085900.0",
"market_cap_\(chosenCurrency)": "1018098455.0",
"available_supply": "83557537.0",
"total_supply": "83557537.0",
"percent_change_1h": "-0.58",
"percent_change_24h": "6.34",
"percent_change_7d": "8.59",
"last_updated": "1472762062"
}
]
"""
If you have a small number of possible keys, you can do the following
struct Price: Decodable {
var value: String
enum CodingKeys: String, CodingKey {
case price_eur
case price_gbp
case price_usd
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
value = try container.decode(String.self, forKey: .price_eur)
} catch {
do {
value = try container.decode(String.self, forKey: .price_gbp)
} catch {
value = try container.decode(String.self, forKey: .price_usd)
}
}
}
}
let data = try! JSONSerialization.data(withJSONObject: ["price_gbp": "10.12"], options: [])
let price = try JSONDecoder().decode(Price.self, from: data)
Otherwise, you will need to parse data manually.