I have a JSON object as such:
{
"red":
{
"a": 1,
"b": 2,
"c": 3
}
"yellow":
{
"a": 1,
"b": 2,
"c": 3
}
"blue":
{
"a": 1,
"b": 2,
"c": 3
}
}
I decode each of these into a Color
object marked as Codable
.
I would like to include the key of the object as a property of the object itself, such that I can differentiate between the keys to provide supplementary information, such as having a function that can provide a color to pair with the object (e.g. for 'red', pair it with 'blue').
How can I include the dictionary key as a property on the Codable
object itself?
CodePudding user response:
A possible way is to add a temporary struct for the color object, then decode the dictionary as [String:Temp]
and map the data to the real struct. As Color
is part of SwiftUI
I named the struct MyColor
struct Temp: Decodable { let a, b, c : Int }
struct Root : Decodable {
let colors : [MyColor]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let colorData = try container.decode([String:Temp].self)
colors = colorData.map{ MyColor(name: $0.key, a: $0.value.a, b: $0.value.b, c: $0.value.c) }
}
}
struct MyColor : Decodable {
let name: String
let a, b, c : Int
}
Then decode
JSONDecoder().decode(Root.self...
However if the keys are static drop the Temp
struct and use this
struct Root : Decodable {
let red : MyColor
let yellow : MyColor
let blue : MyColor
}
struct MyColor : Decodable {
let a, b, c : Int
}
CodePudding user response:
Based on @vadian answer, you could try this approach, using init(from decoder: Decoder)
to decode the json data,
and a Colour
struct
with a name
and id
that you can use.
struct ColorResponse: Decodable {
var red: Colour
var yellow: Colour
var blue: Colour
enum CodingKeys: String, CodingKey {
case red,yellow,blue
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
red = try container.decode(Colour.self, forKey: .red)
red.name = "red"
yellow = try container.decode(Colour.self, forKey: .yellow)
yellow.name = "yellow"
blue = try container.decode(Colour.self, forKey: .blue)
blue.name = "blue"
}
}
struct Colour: Identifiable, Decodable {
let id = UUID()
var name: String = ""
var a, b, c : Int
enum CodingKeys: String, CodingKey {
case a,b,c
}
}
struct ColorView: View {
@State var colour: Colour
var body: some View {
VStack {
Text(colour.name)
Text("\(colour.a)")
Text("\(colour.b)")
Text("\(colour.c)")
}
}
}
struct ContentView: View {
@State var colours: [Colour] = []
var body: some View {
List(colours) { col in
ColorView(colour: col)
}
.onAppear {
let json = """
{
"red": { "a": 1, "b": 2, "c": 3 },
"yellow": { "a": 1, "b": 2, "c": 3 },
"blue": { "a": 1, "b": 2, "c": 3 }
}
"""
// simulated API data
let data = json.data(using: .utf8)!
do {
let results = try JSONDecoder().decode(ColorResponse.self, from: data)
colours.append(results.blue)
colours.append(results.red)
colours.append(results.yellow)
colours.forEach{print("---> colours: \($0)")}
} catch {
print("\n---> decoding error: \n \(error)\n")
}
}
}
}