Помогите правильно создать метод init, чтобы извлечь нужные данные из Json

json

#1

Всем привет! У меня есть JSON в следующем формате. Как мне правильно дописать метод который будет вытаскивать из NSArray subcategories нужные мне значения для каждой категории?

JSON:

“222”:{
“name”:“1 категория”,
“sortOrder”:“29”,
“image”:“some image”,
“iconImage”:“some image”,
“iconImageActive”:“some image”,
“subcategories”:[
{
“id”:“31”,
“iconImage”:“some image”,
“sortOrder”:“2”,
“name”:“подкатегория”,
“type”:“Category”
},
{
“id”:“61”,
“iconImage”:“some image”,
“sortOrder”:“14”,
“name”:“подкатегория 2”,
“type”:“Category”
}

Вот мой код:

struct Category  {

var name: String?
var image: String?
var iconImage: String?
var subcategories: NSArray?

var subCatIconImage: String?
var subCatName: String?

init?(data: NSDictionary){
    guard let name = data["name"] as? String,
        let image = data["image"] as? String,
        let subCat = data["subcategories"] as? NSArray else { return nil }

    self.name = name
    self.image = image
    self.subcategories = subCat
}
}

Заранее спасибо!


Json подскажите как получить доступ к данным внутри структуры
#2

Ваша подкатегория должна быть тоже массивом объектов ваше подструктуры, а не просто массивом, например таким

struct Subcategory {
    var id: Int
    var iconImage: Data
    var sortOrder: Int
    var name: String
    var type: String // лучше Enum тут использовать
    
    init?(data: NSDictionary) {
        ...
    }
}

var subcategories: [Subcategory]

  1. В инициализаторе Category вы извлечёте из data["subcategories"] во временный массив subCat массив словарей [NSDictionary].
    let subCat = data["subcategories"] as? [NSDictionary]

  2. По этому массиву пройдётесь циклом и создадите массив ваших подструктур

    var tempArray = [Subcategory]()
    subCat.forEach { dictItem in
        let subCategoryItem = Subcategory(data: dictItem)
        tempArray.append(subCategoryItem)
    }
    
  3. self.subcategories = tempArray

PS по нициализатору.
Если у вас предполагается сценарий, что одного параметра у Category в джейсоне может не оказаться, или потом предполагается добавить параметров при обновлении, то инициализатор через guard плохая идея: при отсутствии одного из параметров (например нового параметра в модели из обновы) объект не создаться и юзер не увидит данные.

Тут я бы использовал конструкцию через if else с использованием временных переменных в инициализаторе с установкой дефолтных значений, если параметр нельзя извлечь. Пример:

init(dictionary: NSDictionary) {
        var name: String
        var comment: String
        var lastStrike: Date
        var visible: Bool
        var userImage: Data
        
        if let nameInit = dictionary.object(forKey: "name") as? String { name = nameInit } else { name = "" }
        if let commentInit = dictionary.object(forKey: "comment") as? String { comment = commentInit } else {comment = "" }
        if let lastStrikeInit = dictionary.object(forKey: "lastStrike") as? Date {lastStrike = lastStrikeInit} else {lastStrike = Date()}
        if let visibleInit = dictionary.object(forKey: "visible") as? Bool {visible = visibleInit} else {visible = true}
        if let userImageInit = dictionary.object(forKey: "userImage") as? Data {
            userImage = userImageInit
        } else {
            userImage = Data()
        }
        
        self.name = name
        self.comment = comment
        self.lastStrike = lastStrike
        self.visible = visible
        self.userImage = userImage
}

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


#3

А когда научитесь сами парсить json, почитайте про Codable или библиотеку ObjectMapper.
Пока учитесь, это хорошо что сами все это делаете, потом json’ы станут больше, моделей станет больше и каждый раз писать самому парсинг надоест, захотите это все оптимизировать, и вот тут и спасет 2 способа выше.


#4

Совсем забыл ))) Единственный момент, что я описал: если данные только приходят извне, то кодейбл идеален. Если вдруг по какой-то причине старые данные хранятся у юзера, то при обнове с добавлением новых свойств кодейбл также не создаст объект при несовпадении модели и Джейсона. И тогда юзер не увидит или старых данных, или новых, в зависимости от условий. Но это только, узкий частный случай, хотя о котором нужно всегда помнить при парнике JSON или хранении данных в plist (NSDictionary), чтобы потом голову не сломать в поисках что не так :slight_smile:


#5

Большое спасибо за столь развернутый ответ! Очень помогли! Единственное в цикле, где создается массив подструктур в строчке

tempArray.append(subCategoryItem)

Xcode говорит следующее:

Value of optional type ‘Subcategory?’ must be unwrapped to a value of type ‘Subcategory’

пришлось поставить !: tempArray.append(subCategoryItem!) иначе не знаю как быть😬
Если я правильно понял то этот метод должен быть описан непосредственно в моем init Category?

Еще раз спасибо!!!


#6

Спасибо за совет😊 Как только стану более ли менее разбираться обязательно попробую, описанные выше библиотеки!


#7

да, конечно _________