I'm trying data parsing from server with Alamofire.
(I've been trying for 2 days and it's failing.) How can I get Json array Codable type with Alamofire?...
API:
[ { "name": "John Doe", "email": "johndoe@gmail.com", "type": "Lattee", "size": "medium" }, { "name": "Doe", "email": "doe@gmail.com", "type": "Lattee", "size": "small" } ]
now this is my code
in Model.swift
struct OrderList : Codable{
var list : [Order]
}
enum coffeeType: String, Codable{
case cappuccino
case lattee
case espressino
case cortado
}
enum coffeeSize: String, Codable{
case small
case medium
case large
enum CodingKeys: String, CodingKey {
case small = "s"
case medium = "m"
case large = "l"
}
}
struct Order: Codable {
let email: String!
let name : String!
let size : coffeeSize!
let type : coffeeType!
enum CodingKeys: String, CodingKey{
case name = "Name"
case email = "Email"
case type = "Type"
case size = "Size"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
email = try values.decodeIfPresent(String.self, forKey: .email) ?? ""
name = try values.decodeIfPresent(String.self, forKey: .name) ?? "Guest"
size = try values.decodeIfPresent(coffeeSize.self, forKey: .size) ?? .small
type = try values.decodeIfPresent(coffeeType.self, forKey: .type) ?? .lattee
}
}
struct Resource<T: Codable> {
let url : URL
var httpMethod: HTTPMethod = .get
}
I have defined it in various formats such as responseData, responseJSON, and responseCodable, but I keep getting nil or something is missing. I know how to parse with responseJSON. but I want trying to parse by applying Codable... it's too difficult.
---- data parsing ---
func load<T>(resource: Resource<T>, completion: @escaping (Result<T, NetworkError>) -> Void) {
let call = AF.request(myurl,method: resource.httpMethod, parameters: nil).responseJSON{ response in
switch response.result {
case .success(let data):
if let JSON = response.value {
do{
let dataJson = try JSONSerialization.data(withJSONObject: JSON, options: [])
let getInstanceData = try JSONDecoder().decode(T.self, from: dataJson)
print(getInstanceData)
completion(.success(getInstanceData))
}catch{
print(error)
}
}
case .failure(_):
break
}
}
}
The problem with your code is case-sensitive properties. If the API returns these keys Name
, Type
, Email
, Size
, Type
then the Codable object should have the coding Keys to handle keys from API
struct Order: Codable {
let email: String
let name : String
let size : CoffeeSize
let type : CoffeeType
enum CodingKeys: String, CodingKey{
case name = "Name"
case email = "Email"
case type = "Type"
case size = "Size"
}
}
In terms of unwrapping properties, it will depend on the contract between your code and API. If you're pretty sure API will always return size
, type
, name
but email
is nullable. Then you should use the method decodeIfPresent
allows the value of the parsing key is nullable If you're certain about name
, the value is always not null, then you can use decode
normally
and your Order struct will become
struct Order: Codable {
let email: String?
let name : String
let size : CoffeeSize
let type : CoffeeType
enum CodingKeys: String, CodingKey{
case name = "Name"
case email = "Email"
case type = "Type"
case size = "Size"
}
}
Since the API returns this payload:
[ { "name": "John Doe", "email": "johndoe@gmail.com", "type": "Lattee", "size": "medium" }, { "name": "Doe", "email": "doe@gmail.com", "type": "Lattee", "size": "small" } ]
these are keys that your code needs to handle: name
, email
, type
, size
Therefore the Order
struct should be:
struct Order: Codable {
let email: String
let name : String
let size : CoffeeSize
let type : CoffeeType
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
email = try values.decodeIfPresent(String.self, forKey: .email) ?? ""
name = try values.decodeIfPresent(String.self, forKey: .name) ?? "Guest"
let orginalSize = try values.decodeIfPresent(String.self, forKey: .size)
size = CoffeeSize(rawValue: orginalSize ?? CoffeeSize.small.rawValue) ?? .small
let orginalType = try values.decodeIfPresent(String.self, forKey: .type)
type = CoffeeType(rawValue: orginalType ?? CoffeeType.latte.rawValue) ?? .latte
}
}
enum CoffeeType: String, Codable {
case cappuccino
case latte
case espressino
case cortado
}
enum CoffeeSize: String, Codable{
case small
case medium
case large
}
since there is no list
key in the json structure therefore you don't need struct OrderList
. In order to get a list of orders you can simply call
load(resource<[Order]>) { result in
// handle the response but now you will get the list of orders
}
Alternatively, I created a playground to load a json file (data.json) locally and parse it into objects you can have a look at the solution below
func readJsonFile(filename: String) -> String {
guard let fileUrl = Bundle.main.url(forResource: filename, withExtension: "json") else { fatalError() }
guard let jsonData = try? String(contentsOf: fileUrl) else {
return ""
}
return jsonData
}
enum CoffeeType: String, Codable {
case cappuccino
case latte
case espressino
case cortado
}
enum CoffeeSize: String, Codable{
case small
case medium
case large
}
struct Order: Codable {
let email: String
let name : String
let size : CoffeeSize
let type : CoffeeType
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
email = try values.decodeIfPresent(String.self, forKey: .email) ?? ""
name = try values.decodeIfPresent(String.self, forKey: .name) ?? "Guest"
let orginalSize = try values.decodeIfPresent(String.self, forKey: .size)
size = CoffeeSize(rawValue: orginalSize ?? CoffeeSize.small.rawValue) ?? .small
let orginalType = try values.decodeIfPresent(String.self, forKey: .type)
type = CoffeeType(rawValue: orginalType ?? CoffeeType.latte.rawValue) ?? .latte
}
}
func parseJsonFile() {
let jsonStr = readJsonFile(filename: "data")
let jsonData: Data = Data(jsonStr.utf8)
let decoder = JSONDecoder()
do {
let orders = try decoder.decode([Order].self, from: jsonData)
orders.forEach {
print("\($0.name)" + "\n" + "\($0.type.rawValue)" + "\n" + "\($0.size.rawValue)")
}
} catch {
print(error.localizedDescription)
}
}
parseJsonFile()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.