Декодер Swift 4 JSON

Я знаю, что это было рассмотрено в других вопросах, но я следил за ними и все еще в тупике. Вот моя структура JSON:

      {
       "FindBoatResult": {
       "num_boats": 10,
       "boat": [
         {
           "num_segments": 1,
           "segments": [
              {
               "ident": "String",
                "origin" : {
                         "code" : "String"
                 },
           },
         ]
        }

и т. д. ... но это так глубоко, как идет структура. в каждом ответе JSON есть несколько возвратов «сегментов». В Swift у меня есть этот код.

struct Result : Decodable {
    let FindBoatResult : FindBoatResult
}
struct FindBoatResult : Decodable {
    let boats : Boats
    let num_boats : Int
}
struct Boats : Decodable {
    let segments : [Segments]
}
struct Segments : Decodable {
    let ident : String?
    let origin : Origin
}
struct Origin : Decodable {
    let code : String
}

func getBoats() {

let urlString = "http://myApi"
guard let url = URL(string: urlString) else { return }

    URLSession.shared.dataTask(with: url) { (data, response, err) in
        guard let data = data else {return}
        let dataAsString = String(data: data, encoding: .utf8)
        //print(dataAsString)

        do {
            let boats = try
                JSONDecoder().decode(FindBoatResult.self, from: data)

            print(boats)
        } catch {
            print(err) 
        }  
    }.resume()
}

Это терпит неудачу и выдает ошибку, но ошибка печатается как ноль ... поэтому я не могу сказать, что мне не хватает. dataAsString выводит JSON, как и ожидалось, поэтому я знаю, что «данные» — это хорошо.


person captCC    schedule 26.10.2017    source источник
comment
Вы должны распечатать ошибку как print(error)   -  person Vini App    schedule 27.10.2017
comment
... или, чтобы избежать путаницы между ошибкой, переданной dataTask, я бы дал ей уникальное имя, например. do { ... } catch let parseError { print(parseError) }.   -  person Rob    schedule 27.10.2017


Ответы (2)


Я обнаружил пару незначительных проблем. Попробуйте заменить это:

struct FindBoatResult: Decodable {
    let boats: Boats
    let num_boats: Int
}
struct Boats: Decodable {
    let segments: [Segments]
}

с:

struct FindBoatResult: Decodable {
    let boat: [Boat]
    let num_boats: Int
}
struct Boat: Decodable {
    let segments: [Segments]
}

Наконец, декодируйте, используя тип Result (не FindBoatResult):

JSONDecoder().decode(Result.self, from: data)
person Paulo Mattos    schedule 27.10.2017

Расширяя ответ Пауло, я мог бы также предложить, чтобы, если вы застряли с JSON, у которого есть ключи, которые не соответствуют соглашениям Swift для имен свойств, вы должны использовать шаблон CodingKeys для перевода ключей JSON в лучшие имена свойств Swift, например:

struct BoatResult: Decodable {           // I'd simplify this name
    let boatCollection: BoatCollection

    enum CodingKeys: String, CodingKey {
        case boatCollection = "FindBoatResult"
    }
}

struct BoatCollection: Decodable {       // I'd simplify this, too, removing "Find" from the name; verbs are for methods, not properties 
    let boats: [Boat]
    let numberOfBoats: Int

    enum CodingKeys: String, CodingKey {
        case boats = "boat"              // "boat" isn't great property name for an array of boats, so let's map the poor JSON key to better Swift name here
        case numberOfBoats = "num_boats" // likewise, let's map the "_" name with better camelCase property name
    }
}

struct Boat: Decodable {                 // This entity represents a single boat, so let's use "Boat", not "Boats"
    let segments: [Segment]
}

struct Segment: Decodable {              // This entity represents a single segment, so let's use "Segment", not "Segments"
    let identifier: String
    let origin: Origin

    enum CodingKeys: String, CodingKey {
        case identifier = "ident"        // `ident` isn't a common name for identifier, so let's use something more logical
        case origin
    }
}

struct Origin: Decodable {
    let code: String
}

Так, например, используйте множественное число (например, boats), когда вы представляете массив объектов, и используйте CodingKeys для сопоставления вводящего в заблуждение ключа boat JSON с этой ссылкой на массив с более подходящим названием boats. Или, когда у вас есть такой ключ, как num_boats, не думайте, что вам нужно использовать это плохое имя в своем свойстве Swift и использовать что-то лучшее, например numberOfBoats (или count или что-то еще), и потерять синтаксис _, который очень небыстрый.

Очевидно, что если вы контролируете дизайн JSON, вы можете просто исправить некоторые из этих плохо выбранных имен ключей, но даже если вы решите, что хотите, чтобы ваш веб-сервис использовал синтаксис _, используйте CodingKeys для убедитесь, что ваши объекты Swift соблюдают соглашение camelCase.

person Rob    schedule 27.10.2017
comment
Я не понимаю, почему вы не можете опубликовать вопрос. Если это S.O. вопрос, затем найдите другое место для публикации вопроса. Если это проблема работы/конфиденциальности, то анонимизируйте вопрос в достаточной степени, чтобы решить эти проблемы. Но пытаться диагностировать вашу проблему здесь, в комментариях, не место. Вы должны удалить комментарии выше. - person Rob; 30.12.2017