Декодирование массива JSON с использованием Codable в Swift 5 - PullRequest
0 голосов
/ 24 апреля 2019

Я потратил немало времени, просматривая это и просматривая примеры в SO / Web, но я не вижу, что я сделал неправильно.

REST-сервер возвращает мне следующий JSON:

{
        "data":{
            "customers":
            [
              {
                    "customerId":27,
                    "customerName":"Dispatch Tool Customer 1"
              }
            ]
        },
        "error":"<null>",
        "ok":1
    }

И следующие классы, представляющие эти данные:

public class ResponseBase : Codable
{
    public var ok : Int?
    public var error : String?
}

public class CustomersResponse : ResponseBase
{
    public class Customer : Codable
    {
        public var customerName : String?
        public var customerId : Int?

        enum CodingKeys: String, CodingKey {
            case customerId = "customerId"
            case customerName = "customerName"
        }

        required public init(from decoder: Decoder) throws {
            let values = try decoder.container(keyedBy: CodingKeys.self)
            customerId = try values.decodeIfPresent(Int.self, forKey: .customerId)
            customerName = try values.decodeIfPresent(String.self, forKey: .customerName)
        }
    }

    public class Data : Codable
    {
        public var customers : [Customer]?

        enum CodingKeys: String, CodingKey {
            case customers = "customers"
        }

        required public init(from decoder: Decoder) throws {
            let values = try decoder.container(keyedBy: CodingKeys.self)
            customers = try values.decodeIfPresent([Customer].self, forKey: .customers)
        }
    }

    public var data : Data?


    enum CodingKeys: String, CodingKey {
        case data = "data"
        case error = "error"
        case ok = "ok"
    }

    required public init(from decoder: Decoder) throws {
        super.init()
        data = try Data(from: decoder)
    }
}

А следующий код использует Alamofire 4.0 для вызова сервера, а затем обрабатывает ответ, используя возможности декодирования JSON в Swift:

Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: [:]).responseJSON
            {
                response in
                print(response)
                print("=============================================================")

                do {
                    let decoder = JSONDecoder()

                    let model = try decoder.decode(CustomersResponse.self, from: response.data!)
                    print("done model")
                } catch let error {
                    print("ERROR")
                    print(error)
                }
            }

"ok" и "error" декодируются правильно. «данные» тоже декодируются, но «клиенты» всегда равны нулю. Изначально у меня были классы без инициализаторов и CodingKeys, но, конечно, я получаю тот же результат.

Я распечатываю ответ и вижу, что JSON корректно отключается (хотя я не понимаю, почему он не распечатывается в стандартном формате JSON - почему Alamofire заменяет [на (и: with =? JSON с сервера правильно отформатирован JSON).

Я не уверен, что еще я могу попробовать.

Ответы [ 2 ]

0 голосов
/ 24 апреля 2019

Вот решение, которое работает для меня.Спасибо тем, кто ответил и указал мне правильное направление:

struct Customer: Decodable
{
    let customerId: Int?
    let customerName: String?
}


struct CustomerData : Decodable
{
    let customers : Array<Customer>?
}


struct ServerResponse<T:Decodable> : Decodable
{
    let data : T?
    let error : String?
    let ok : Bool?
}

typealias CustomersResponse = ServerResponse<CustomerData>

(Примечание: у меня был Int в исходном вопросе, но на самом деле это Bool)

0 голосов
/ 24 апреля 2019

Прежде всего, вы используете ответ json от Alamofire, что означает, что результат уже преобразован в объект json или словарь в соглашении по какао.При использовании codable для декодирования / кодирования значение in / out должно быть типом данных.Вот почему вы видите значение с чем-то похожим на уже декодированное.

Во-вторых, я не уверен, чего вы хотите достичь, поэтому я опишу самый простой формат.

struct Customer: Decodable{
    let customerId: Int
    let customerName: String
}

struct ServerResponse: Decodable {
    let ok: Int
    let error: String?
    let data: [Customer]
}

Этоэто самый простой способ расшифровать ответ.Для ServerResponse вместо его подкласса я бы также рекомендовал использовать тип Generic, чтобы вы могли

struct ServerResponse<T>: Decodable {
    let ok: Int
    let error: String?
    let data: T
}
typealias CustomerResponse = ServerResponse<[Customer]>

декодировать любой ответ сервера (из-за того, что применяется только в том случае, если все ваши ответы сервера хорошо отформатированы)

...