Unable to parse using codable structs for following json
{
"data": {
"listDeviceStateTable": {
"items": [
{
"Data": "{'state': -1, 'remainSec': 0}",
"PK": "DEVICE#144b584b-xxxx-xxxx-xxxx-1e584bdb1e8c",
"SK": "Station1"
},
{
"Data": "{'state': -1, 'remainSec': 0}",
"PK": "DEVICE#144b584b-xxxx-xxxx-xxxx-1e584bdb1e8c",
"SK": "Station2"
}
]
}
}
}
Error :
APIError: keyNotFound key CodingKeys(stringValue: "data", intValue: nil) Caused by: keyNotFound(CodingKeys(stringValue: "data", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "data", intValue: nil) ("data").", underlyingError: nil)))
Model:
//MARK: DeviceState
struct DeviceState:Codable {
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Codable {
let listDeviceStateTable: ListDeviceStateTable
}
// MARK: - ListDeviceStateTable
struct ListDeviceStateTable: Codable {
let items: [Item]
}
// MARK: - Item
struct Item: Codable {
let data, pk, sk: String
enum CodingKeys: String, CodingKey {
case data = "Data"
case pk = "PK"
case sk = "SK"
}
}
CodePudding user response:
works well for me. This is the test code I used to show how to decode the json data into your structs. Of course Item->data
is here decoded as a String, as per the json you show.
struct ContentView: View {
@State var devState: DeviceState?
var body: some View {
Group {
if let devFirst = devState?.data.listDeviceStateTable.items.first {
Text("pk: \(devFirst.pk)")
}
}
.onAppear {
let json = """
{
"data": {
"listDeviceStateTable": {
"items": [
{
"Data": "{'state': -1, 'remainSec': 0}",
"PK": "DEVICE#144b584b-xxxx-xxxx-xxxx-1e584bdb1e8c",
"SK": "Station1"
},
{
"Data": "{'state': -1, 'remainSec': 0}",
"PK": "DEVICE#144b584b-xxxx-xxxx-xxxx-1e584bdb1e8c",
"SK": "Station2"
}
]
}
}
}
"""
let data = json.data(using: .utf8)!
do {
devState = try JSONDecoder().decode(DeviceState.self, from: data)
print(devState)
} catch {
print("\(error)")
}
}
}
}
struct DeviceState:Codable {
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Codable {
let listDeviceStateTable: ListDeviceStateTable
}
// MARK: - ListDeviceStateTable
struct ListDeviceStateTable: Codable {
let items: [Item]
}
// MARK: - Item
struct Item: Codable {
let data, pk, sk: String
enum CodingKeys: String, CodingKey {
case data = "Data"
case pk = "PK"
case sk = "SK"
}
}
to decode Data
into something else than a String
, eg a ItemData
, you need to remove the double quotes,
and replace the single quote with double quotes.
eg. "Data": "{'state': -1, 'remainSec': 0}",
to "Data": {"state": -1, "remainSec": 0},
and use the following decoding code:
// MARK: - Item
struct Item: Codable {
let data: ItemData
let pk, sk: String
enum CodingKeys: String, CodingKey {
case data = "Data"
case pk = "PK"
case sk = "SK"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
pk = try container.decode(String.self, forKey: .pk)
sk = try container.decode(String.self, forKey: .sk)
do {
let theString = try container.decode(String.self, forKey: .data)
let json = theString.replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: "'", with: "\"")
data = try JSONDecoder().decode(ItemData.self, from: json.data(using: .utf8)!)
} catch DecodingError.typeMismatch {
data = ItemData(state: 0, remainSec: 0) // <-- todo
}
}
}
struct ItemData: Codable {
let state, remainSec: Int
}
CodePudding user response:
You need one more struct to decode data inside Item like this
struct Item: Codable {
let data: ItemData
let pk, sk: String
enum CodingKeys: String, CodingKey {
case data = "Data"
case pk = "PK"
case sk = "SK"
}
}
struct ItemData: Codable {
let state, remainSec: Int
}