#json #swift #jsondecoder
#json #swift #jsondecoder
Вопрос:
Веб-сайт API, который я использовал. Я создал учетную запись -> Документация -> Brawlers, если вы хотите попробовать
Вот часть JSON, которую я хочу проанализировать. Я хочу напечатать имя внутри массива «starPowers», а также массива «гаджеты».
{
"items":[
{
"id":16000014,
"name":"BO",
"starPowers":[
{
"id":23000090,
"name":"CIRCLING EAGLE"
},
{
"id":23000148,
"name":"SNARE A BEAR"
}
],
"gadgets":[
{
"id":23000263,
"name":"SUPER TOTEM"
},
{
"id":23000289,
"name":"TRIPWIRE"
}
]
}
]
}
Я попробовал этот первый способ анализа JSON, который сработал, но я не смог найти способ напечатать «name» внутри массива «StarPower» или «gadgets».
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let apiToken = "abcdefg"
if let url = URL(string: "https://api.brawlstars.com/v1/brawlers") {
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Bearer (apiToken)", forHTTPHeaderField: "authorization")
request.addValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
} else {
guard let data = data else {return}
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as AnyObject
print(jsonResult)
if let items = jsonResult["items"] as? NSArray {
for item in items as [AnyObject] {
if let name = item["name"] {
print(name!)
}
if let gadgets = item["gadgets"] {
print(gadgets!)
}
if let starPowers = item["starPowers"]{
print(starPowers!)
}
}
}
} catch {
print("JSON processing failed: (error.localizedDescription)")
}
}
}.resume()
} else {
print("Something went wrong")
}
}
}
Итак, я добавил еще один файл с этими данными:
struct StarPowers: Codable {
let starPowerName: String
}
struct Gadgets: Codable {
let gadgetName: String
}
struct Items: Codable {
let name: String
let starPowers: [StarPowers]
let gadgets: [Gadgets]
}
И заменил код внутри инструкции do следующим образом, но он вернул «Сбой обработки JSON: данные не могут быть прочитаны, потому что они отсутствуют». (Оператор Catch)
let items = try JSONDecoder().decode(Items.self, from: data)
print(items.name.first ?? "")
Я все еще довольно новичок в Swift в целом, а также в StackOverflow, поэтому буду очень признателен за любую помощь или отзыв. Спасибо!
Ответ №1:
При использовании Codable
API требуется моделировать все данные, которые вы видите. Итак, этот JSON:
{
"items":[
{
"id":16000014,
"name":"BO",
"starPowers":[
{
"id":23000090,
"name":"CIRCLING EAGLE"
},
{
"id":23000148,
"name":"SNARE A BEAR"
}
],
"gadgets":[
{
"id":23000263,
"name":"SUPER TOTEM"
},
{
"id":23000289,
"name":"TRIPWIRE"
}
]
}
]
}
должно привести к этим структурам:
//This represents one star power
struct StarPower: Codable {
let id: Int
let name: String
}
//This represents one gadget
struct Gadget: Codable {
let id: Int
let name: String
}
//This represents one "item" (I think they're brawlers, but I didn't make an account so I can't confirm what the API calls them
struct Item: Codable {
let id: Int
let name: String
let starPowers: [StarPower]
let gadgets: [Gadget]
}
Однако предоставленный вами отрывок на самом деле имеет тип [String:[Item]]
(словарь (он же объект JSON) с одним строковым ключом, значение которого представляет собой массив элементов. Вы могли бы создать Codable
структуру для обработки этого, или вы можете просто сделать это:
let decoded = try! JSONDecoder().decode([String:[Item]].self, from: data)
let items = decoded["items"]!
//items is an array of type [Item]
// Using your example, it would only have one element.
let element = items.first!
for starPower in element.starPowers {
print(starPower.name)
}
for gadget in element.gadgets {
print(gadget.name)
}
(имейте в виду, что я принудительно разворачиваю и try
редактирую, потому что я предполагаю, что выборка данных прошла нормально и что это правильно. Вероятно, вам следует проверить эти предположения и использовать такие конструкции, как if-let
и do-catch
)
Ответ №2:
Назовите свойства ваших кодируемых структур точно так же, как ключи в соответствующем JSON:
struct StarPower: Codable {
let name: String
}
struct Gadget: Codable {
let name: String
}
struct Item: Codable {
let name: String
let starPowers: [StarPower]
let gadgets: [Gadget]
}
Декодер не смог найти данные, потому что «gadgetName» и «starPowerName» не являются частью предоставленного вами JSON.
Еще один совет: используйте единственное число при именовании структур, поэтому одним из элементов гаджетов является гаджет. декодеру все равно, как вы называете свои структуры, его интересуют только свойства