Как правильно декодировать вложенные объекты JSON с помощью структур Swift - PullRequest
0 голосов
/ 16 февраля 2019

Намерение:

Получать данные о ценах криптовалюты через Coinmarketcap API , декодировать их в пользовательские структуры в SWIFT и потенциально сохранять эти данные в базе данных (либо CoreData, либоSQLite).

Контекст:

Я получаю следующую ошибку на JSONDecoder().decode:

Error serializing json: typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "status", intValue: nil), _DictionaryCodingKey(stringValue: "credit_count", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a number instead.", underlyingError: nil))

Вопросы:

  1. Как правильно интерпретировать эту ошибку?Что я неправильно декодирую?
  2. Правильно ли отформатированы данные, которые я получаю?Не похоже на правильный JSON.

Код:

import UIKit
import PlaygroundSupport


// Defining structures

struct RootObject: Decodable {
    let status: [String: StatusObject?]
    let data: DataObject?
}

struct StatusObject: Decodable {
    let credit_count: Int?
    let elapsed: Int?
    let error_code: Int?
    let timestamp: String?
}

struct DataObject: Decodable {
    let amount: Int?
    let id: Int?
    let last_updated: String?
    let name: String?
    let quote: [QuoteObject]?
    let symbol: String?
}

struct QuoteObject: Decodable {
    let usd: String?
}

struct usdObject: Decodable {
    let last_updated: String?
    let price: String?
}


//Configuring URLSession

let config = URLSessionConfiguration.default

config.httpAdditionalHeaders = ["X-CMC_PRO_API_KEY": "<removed>",
                                "Accept": "application/json",
                                "Accept-Encoding": "deflate, gzip"]

let session = URLSession(configuration: config)

let url = URL(string: "https://sandbox-api.coinmarketcap.com/v1/tools/price-conversion?convert=USD&amount=1&symbol=BTC")!


//Making and handling a request

let task = session.dataTask(with: url) { data, response, error in

    guard error == nil else {
        print ("error: \(error!)")
        return
    }

    guard let content = data else {
        print("No data")
        return
    }

    //Serializing and displaying the received data
    guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [String: Any]
    else {
        print("Not containing JSON")
        return
    }

    print(json)

    //Trying to decode
    do {
        let prices = try JSONDecoder().decode(RootObject.self, from: data!)
        print(prices)
    } catch let decodeError {
        print("Error serializing json:", decodeError)
    }
}

task.resume()

Ответ данных и ошибка:

["status": {
    "credit_count" = 1;
    elapsed = 6;
    "error_code" = 0;
    "error_message" = "<null>";
    timestamp = "2019-02-16T11:10:22.147Z";
}, "data": {
    amount = 1;
    id = 1;
    "last_updated" = "2018-12-22T06:08:23.000Z";
    name = Bitcoin;
    quote =     {
        USD =         {
            "last_updated" = "2018-12-22T06:08:23.000Z";
            price = "3881.88864625";
        };
    };
    symbol = BTC;
}]
Error serializing json: typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "status", intValue: nil), _DictionaryCodingKey(stringValue: "credit_count", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a number instead.", underlyingError: nil))

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

Правильно сериализованный JSON:

{
    "status": {
        "timestamp": "2019-02-16T18:54:05.499Z",
        "error_code": 0,
        "error_message": null,
        "elapsed": 6,
        "credit_count": 1
    },
    "data": {
        "id": 1,
        "symbol": "BTC",
        "name": "Bitcoin",
        "amount": 1,
        "last_updated": "2018-12-22T06:08:23.000Z",
        "quote": {
            "USD": {
                "price": 3881.88864625,
                "last_updated": "2018-12-22T06:08:23.000Z"
            }
        }
    }
}

1 Ответ

0 голосов
/ 16 февраля 2019

Существует много проблем в структурах.

Основная проблема заключается в том, что значение для data является словарем, который декодируется в структуру, а не в другой словарь.Другие проблемы заключаются в том, что тип id равен String, а price равен Double.

API, такие как Coinmarketcap, отправляют надежные данные, поэтому не объявляйте все как необязательные.Удалите вопросительные знаки.

Структуры ниже способны декодировать JSON.Кавычки декодируются в словарь, потому что ключи меняются.Добавьте стратегию декодирования ключа .convertFromSnakeCase, чтобы получить ключи camelCased .Даты декодируются как Date путем добавления соответствующей стратегии декодирования даты.

Я удалил все эти избыточные ...Object вхождения, кроме DataObject, поскольку структура Data уже существует.

struct Root: Decodable {
    let status: Status
    let data: DataObject
}

struct Status: Decodable {
    let creditCount: Int
    let elapsed: Int
    let errorCode: Int
    let timestamp: Date
}

struct DataObject: Decodable {
    let amount: Int
    let id: String
    let lastUpdated: Date
    let name: String
    let quote: [String:Quote]
    let symbol: String
}

struct Quote: Decodable {
    let lastUpdated: Date
    let price: Double
}


//Trying to decode
do {
    let dateFormatter = DateFormatter()
    dateFormatter.locale = Locale(identifier: "en_US_POSIX")
    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    decoder.dateDecodingStrategy = .formatted(dateFormatter)
    let result = try decoder.decode(Root.self, from: data!)
    let quotes = result.data.quote
    for (symbol, quote) in quotes {
        print(symbol, quote.price)
    }
} catch {
    print(error)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...