Alamofire + Decodable — как превратить responseJSON в NSData

Я почти уверен, что моя проблема легко решается, но я не могу найти никакого решения. Итак, у меня есть запрос Alamofire, и у меня проблемы с обработкой типов данных. У меня так много «распечаток», чтобы шаг за шагом проверить, какие данные у меня есть.

Alamofire.request(URL, method: .get, headers: headers).responseJSON { response in
            switch responseJSON.result {
            case .success(let value):
                print(type(of: value))  //__NSDictionaryI
                print(value)
                print(type(of:responseJSON))  //DataResponse<Any>
                print(responseJSON) . //SUCCESS: {"billing_addresses" =     (...
                print(responseJSON.value as Any) . //Optional({...
                //print(responseJSON.value as! [[String:Any]]) . //Could not cast value of type '__NSDictionaryI' (0x10b9fb508) to 'NSArray' (0x10b9fb008).

                do {
                    let decoder = JSONDecoder()
                    let model = try decoder.decode(Info.self, from: value as! Data) //Decode JSON Response Data
                    print(model.id)
                } catch let parsingError {
                    print("Error", parsingError)
                }

Теперь у меня ошибка: **Could not cast value of type '__NSSingleEntryDictionaryI' (0x10d240f78) to 'NSData' (0x10d241090).**

значение responseJSON:

(Я не уверен, что это значение правильное, потому что, когда я проверяю в Postman, все строки заключаются в двойные кавычки, а значение «is_default» равно true/false, а не 0/1. Но в Xcode у меня есть это в консоли.. Так может проблема в responseJSON?..)

А адресов может быть ноль, а может быть и несколько.

{
"id": 40128,
"username": "test6",
"email": "[email protected]",
"billing_addresses": [
    {
        "address_name": null,
        "country_code": "US",
        "first_name": "Ted",
        "last_name": "Qqqq",
        "company_name": "",
        "address_line1": "308 Sea Lane",
        "address_line2": "",
        "city": "QQQQ",
        "state": "FL",
        "postcode": "32000",
        "email_address": "[email protected]",
        "phone_number": "11111111",
        "is_default_for_billing": true
    }
],
"shipping_addresses": [
    {
        "address_name": null,
        "country_code": "US",
        "first_name": "Ted",
        "last_name": "Qqqq",
        "company_name": "",
        "address_line1": "308 Sea Lane",
        "address_line2": "",
        "city": "QQQQ",
        "state": "FL",
        "postcode": "32000",
        "is_default_for_shipping": true
    }
]

}

А вот модель

struct Info : Decodable {
                    let id: Int
                    let email: String
                    let username: String
                    let billing_addresses: Billings
                    let shipping_addresses: Shippings
                }
                struct Billings: Decodable{
                    let address_name: String
                    let country_code: String
                    let first_name: String
                    let last_name: String
                    let company_name: String
                    let address_line1: String
                    let address_line2: String
                    let city: String
                    let state: String
                    let postcode: String
                    let email_address: String
                    let phone_number: String
                    let is_default_for_billing: Bool

                }
                struct Shippings:Decodable{
                    let address_name: String
                    let country_code: String
                    let first_name: String
                    let last_name: String
                    let company_name: String
                    let address_line1: String
                    let address_line2: String
                    let city: String
                    let state: String
                    let postcode: String
                    let is_default_for_shipping: Bool

                }

Если я попытаюсь использовать SwiftyJSON с value в качестве параметра, у меня возникнет ошибка, что Any не может быть Data, и я действительно не знаю, что мне делать.


person nastassia    schedule 23.08.2018    source источник
comment
Вам не хватает определений массивов в ваших структурах? например let shipping_addresses: Shippings[] ??   -  person Piotr Kula    schedule 23.08.2018


Ответы (1)


responseJSON.result.value возвращает десериализованный тип коллекции, в вашем случае словарь [String:Any]

Чтобы использовать JSONDecoder, вам нужны необработанные данные, которые находятся в response.data.

let model = try decoder.decode(Info.self, from: response.data) //Decode JSON Response Data

Учтите, что вы столкнетесь с ошибками декодирования: billing_addresses и shipping_addresses — это массивы

let billing_addresses: [Billings] 
let shipping_addresses: [Shippings] // better name both structs in singular form (Billing, Shipping)

и несколько значений могут быть числами, а не строками.

В любом случае рекомендуется использовать стратегию декодирования ключа convertFromSnakeCase, чтобы избавиться от уродливых имен в змеином регистре.

Редактировать:

Вот ваши структуры с именами в верблюжьем регистре и формами в единственном числе, вы должны добавить

decoder.keyDecodingStrategy = .convertFromSnakeCase

struct Info : Decodable {
    let id: Int
    let email: String
    let username: String
    let billingAddresses: [Billing]
    let shippingAddresses: [Shipping]
}

struct Billing : Decodable {
    let addressName: String?
    let countryCode, firstName, lastName, companyName: String
    let addressLine1, addressLine2, city, state, postcode: String
    let emailAddress, phoneNumber: String
    let isDefaultForBilling: Bool

}
struct Shipping : Decodable {
    let addressName: String?
    let countryCode, firstName, lastName, companyName: String
    let addressLine1, addressLine2, city, state, postcode: String
    let isDefaultForShipping: Bool
}
person vadian    schedule 23.08.2018
comment
если я изменю строку на "from: response.data!" то у меня возникает ошибка: Error dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: данные недействительны JSON., baseError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 Текст JSON не начинался с массива или объект и параметр, разрешающий фрагменты, не установлены. UserInfo={NSDebugDescription=текст JSON не начинался с массива или объекта, и параметр, разрешающий фрагменты, не установлен.}))) - person nastassia; 23.08.2018
comment
Большое спасибо за замечание, что в структурах должны быть массивы! - person nastassia; 23.08.2018
comment
Затем попробуйте responseData вместо responseJSON и передайте response.result.value в качестве аргумента в JSONDecoder. - person vadian; 23.08.2018
comment
Я могу передать только responseData.result.value! и получаю ошибку Error dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))'Если я прохожу response.result.value Xcode показывает предупреждение Use of unresolved identifier 'response' - person nastassia; 23.08.2018
comment
Нет, пожалуйста, прочитайте мой последний комментарий: это response.result.value, а не responseData.result.value. И в API Alamofire это headers).responseData { response - person vadian; 23.08.2018
comment
В данном случае Error valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "billing_addresses", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "address_name", intValue: nil)], debugDescription: "Expected String value but found null instead.", underlyingError: nil)). А если распечатать: print(response) //SUCCESS: 655 bytes и все - person nastassia; 23.08.2018
comment
и распечатать print(response.result.value) //Optional(655 bytes) - person nastassia; 23.08.2018
comment
В принципе, это хорошо, но вы получили одну из предсказанных ошибок декодирования. Сделать address_name в Billings и Shippings необязательными: let address_name: String? - person vadian; 23.08.2018
comment
И еще есть Error dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}))) - person nastassia; 23.08.2018
comment
Если вы получите ошибку декодирования, вы не сможете одновременно получить поврежденные данные. - person vadian; 23.08.2018
comment
Большое спасибо, @vadian, я так ценю, что ты пытаешься мне помочь! - person nastassia; 23.08.2018
comment
Предполагается, что декодер работает, если оба свойства address_name являются необязательными (String?). Я добавил структуры в стиле camelCased и быстро протестировал их. - person vadian; 23.08.2018
comment
о боже! Я только что прокомментировал эти 2 строки с именами адресов, и НАКОНЕЦ-ТО РАБОТАЕТ! О, ты действительно спас меня! БЛАГОДАРЮ ВАС!!! Думаю лучше пусть будет пустая строка и все будет ок - person nastassia; 23.08.2018
comment
Спасибо за ваш ответ! Очень понравилось! вы сэкономили мне кучу времени! - person user2525211; 09.08.2020