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]