Преобразование данных API JSON в структуру Swift

#json #swift #codable

#json #swift #кодируемый

Вопрос:

Я использую Swift впервые, и я хотел бы иметь возможность обрабатывать некоторую информацию из ответа API в полезный объект Swift.

У меня есть (например) следующие данные, возвращающиеся из моего API:

 {
    data: [{
       id: 1,
       name: "Fred",
       info: {
           faveColor: "red",
           faveShow: "Game of Thrones",
           faveIceCream: "Chocolate",
           faveSport: "Hockey",
       },
       age: "28",
       location: "The Moon",
    },{
        ...
    }]
}
  

В swift я data возвращаюсь из API. Я получаю первый объект, конвертирую его и получаю к нему доступ следующим образом:

 let json = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
let dataParentNode = json["data"] as! [[String:Any]]
let firstObject = dataParentNode[0]

let _id = firstObject["id"] as? String ?? "0"
let _name = firstObject["name"] as? String ?? "Unknown"
  

Это нормально, пока я не захочу начать обработку подобъектов, принадлежащих первому объекту, поэтому я придумал следующие struct действия, чтобы попытаться сделать это более чистым.

Пожалуйста, обратите внимание — мне не нужно обрабатывать все возвращаемые данные JSON, поэтому я хочу преобразовать их в то, что мне нужно в структурах

 struct PersonInfo : Codable {
    let faveColor: String?
    let faveShow: String?
}

struct Person : Codable {
   let id: String?
   let name: String?
   let info: PersonInfo?
}
  

Когда я беру это:

 let json = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
let dataParentNode = json["data"] as! [[String:Any]]
let firstObject = dataParentNode[0]
  

а затем попытайтесь преобразовать firstObject в Person или firstObject["info"] в PersonInfo , кажется, я не могу заставить его работать (я получаю nil ).

 let personInfo = firstObject["info"] as? PersonInfo
  

Кто-нибудь может посоветовать, пожалуйста? Мне просто нужно разобраться с получением данных ответа API и сопоставлением их с заданной структурой (с подобъектами), игнорируя ключи, которые мне не нужны.

Комментарии:

1. let personInfo = firstObject as? PersonInfo , firstObject as? PersonInfo равно нулю, потому firstObject что не переносится как PersonInfo, в лучшем случае это словарь. let personInfo = PersonInfo(faveColor: firstObject["faveColor"] as? String ?? "Unknown color", faveShow: firstObject["faveColor"] as? String ?? "Unknown show")

2. @Larme — извините, это была опечатка. firstObject это первый результат в массиве из data JSON, поэтому он должен быть: let personInfo = firstObject["info"] as? PersonInfo в моем OP

3. let personInfoDict = firstObject["info"] as? [String: Any] , и let personInfo = PersonInfo(faveColor: personInfoDict["faveColor"] as? String ?? "Unknown color", faveShow: personInfoDict["faveColor"] as? String ?? "Unknown show")

Ответ №1:

Для этого вы можете просто использовать decode(_:from:) function of JSONDecoder :

 let decoder = JSONDecoder()
do {
    let decoded = try decoder.decode([String: [Person]].self, from: data)
    let firstObject = decoded["data"]?.first
} catch {
    print(error)
}
  

Еще лучше, если вы можете добавить к своей модели другую структуру, подобную этой:

 struct PersonsData: Codable {
    let data: [Person]
}
  

И сопоставьте свой JSON с использованием этого типа:

 let decoder = JSONDecoder()
do {
    let decoded = try decoder.decode(PersonsData.self, from: data)
    let firstObject = decoded.data.first
} catch {
    print(error)
}
  

Обновление: ваша структура Person может потребовать небольшого изменения, поскольку свойство id в вашем JSON является целым числом.

Итак, это закончится так:

 struct Person : Codable {
   let id: Int?
   let name: String?
   let info: PersonInfo?
}
  

Комментарии:

1. Еще раз спасибо за это. Это был ответ для меня. Я пытался сделать что-то подобное, но все равно получил ошибку. Оказывается, я не заметил потенциального изменения в ответе API, которое мне нужно было учесть. Теперь все работает и код намного чище!