Home > Software engineering >  Swift Error JSONDecoder keyNotFound When trying to Create Object
Swift Error JSONDecoder keyNotFound When trying to Create Object

Time:11-22

Need to create object from JSON Object from API

JSON Response from API

[
  {
    "CUSTOMER_ID": 1,
    "CUSTOMER_CODE": "123",
    "PERSONS": [
      {
        "FIRST_NAME": "Patrick",
        "MID_NAME": "A.",
        "LAST_NAME": "L."
      },
      {
        "FIRST_NAME": "Nadine",
        "MID_NAME": "N.",
        "LAST_NAME": "Ricordo"
      }
    ]
  },
  {
    "CUSTOMER_ID": 2,
    "CUSTOMER_CODE": "456",
    "PERSONS": [
      {
        "FIRST_NAME": "Giulio",
        "MID_NAME": "G.",
        "LAST_NAME": "A."
      }
    ]
  },
  {
    "CUSTOMER_ID": 3,
    "CUSTOMER_CODE": "789",
    "PERSONS": [
      {
        "FIRST_NAME": "Vanessa",
        "MID_NAME": null,
        "LAST_NAME": "B"
      },
      {
        "FIRST_NAME": "Nadine",
        "MID_NAME": "D.",
        "LAST_NAME": "Ricordo"
      }
    ]
  }
]

I also created struct like below

struct Customer : Codable {
    var CUSTOMER_ID: Int
    var CUSTOMER_CODE: String
    var PERSONS: Person
}

struct Person : Codable {
    var FIRST_NAME: String
    var MID_NAME: String? // can be null
    var LAST_NAME: String
}

My ViewModel

class CustomerListViewModel: ObservableObject {
    
    @Published var customers: [CustomerViewModel] = []
    
    func getAllCustomers() {
        
        let defaults = UserDefaults.standard
        guard let token = defaults.string(forKey: "jsonwebtoken") else {
            return
        }
        
        Webservice().getAllCustomers(token: token) { (result) in
            switch result {
                case .success(let customers):
                    DispatchQueue.main.async {
                        self.customers = customers.map(CustomerViewModel.init)
                    }
                case .failure(let error):
                    print(error.localizedDescription) // output: AppName.NetworkError error 2.
            }
        }
        
    }
    
}


struct CustomerViewModel {
    
    let customer: Customer
    
    let id = UUID()
    
    var CUSTOMER_ID: Int {
        return customer.CUSTOMER_ID
    }
    
    var CUSTOMER_CODE: String {
        return customer.CUSTOMER_CODE
    }
    
    var PERSONS: Person {
        return customer.PERSONS
    }

Decoding at Webservice class

class Webservice {
        
    func getAllCustomers(token: String, completion: @escaping (Result<[Customer], NetworkError>) -> Void) {
        
        guard let url = URL(string: "https://myapi") else {
            completion(.failure(.invalidURL))
            return
        }
        
        var request = URLRequest(url: url)
        request.addValue(token, forHTTPHeaderField: "access-token")
        
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            
            guard let data = data, error == nil else {
                completion(.failure(.noData))
                return
            }
            
            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            
            do {
                let customers = try decoder.decode(Array<Customer>.self, from: data)
                print(customers)
                completion(.success(customers))
            } catch let error {
                print("[Webservice] Decoding Error")
                print(error)
                completion(.failure(.decodingError))
                return
            }
        }.resume()
    }
[...]
}

ERROR

[Webservice] Decoding Error keyNotFound(CodingKeys(stringValue: "CUSTOMER_ID", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"CUSTOMER_ID\", intValue: nil) (\"CUSTOMER_ID\"), with divergent representation cUstomerId, converted to c_ustomer_id.", underlyingError: nil))

Later I want for example print(myCustomerViewModel.PERSONS[i].FIRST_NAME) using my ViewModel I also need null handling if a value in my JSON is null like for example at MID_NAME of "Vanessa"

UPDATE 1

After deleting decoder.keyDecodingStrategy = .convertFromSnakeCase

I'm getting following error:

keyNotFound(CodingKeys(stringValue: "CUSTOMER_ID", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"CUSTOMER_ID\", intValue: nil) (\"CUSTOMER_ID\").", underlyingError: nil))

CodePudding user response:

Delete this line

decoder.keyDecodingStrategy = .convertFromSnakeCase

as you actually don't convert the keys from snake case (better add CodingKeys to avoid those ugly capitalized names).

And to get rid of another error declare PERSONS as array

var PERSONS: [Person]
  • Related