Home > Blockchain >  Parsing issue : APIError: keyNotFound key CodingKeys(stringValue: "data", intValue: nil)
Parsing issue : APIError: keyNotFound key CodingKeys(stringValue: "data", intValue: nil)

Time:08-09

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
}
  • Related