Swift — декодирование глубоко вложенного словаря

Я так близок, но я борюсь с очень простой функцией, позволяющей мне получить доступ к точке данных, глубоко вложенной в мой JSON. Пример, который я использую, относится к API направлений Google.

Пример JSON (из GMapsAPI):

{
  "geocoded_waypoints" : [
  {
     "geocoder_status" : "OK",
     "partial_match" : true,
     "place_id" : "ChIJ960bMolw44kRQcGOlOZQ-r8",
     "types" : [ "premise" ]
  },
  {
     "geocoder_status" : "OK",
     "partial_match" : true,
     "place_id" : "EiMxMTggU2FsZW0gU3QsIEJvc3RvbiwgTUEgMDIxMTMsIFVTQSIaEhgKFAoSCSvDfDSJcOOJEbQanF0WxROfEHY",
     "types" : [ "street_address" ]
  }
],
"routes" : [
  {
     "bounds" : {
        "northeast" : {
           "lat" : 42.3647252,
           "lng" : -71.0555085
        },
        "southwest" : {
           "lat" : 42.3644965,
           "lng" : -71.05552419999999
        }
     },
     "copyrights" : "Map data ©2018 Google",
     "legs" : [
        {
           "distance" : {
              "text" : "82 ft",
              "value" : 25
           },
           "duration" : {
              "text" : "1 min",
              "value" : 11
           },
           "end_address" : "118 Salem St, Boston, MA 02113, USA",
           "end_location" : {
              "lat" : 42.3647252,
              "lng" : -71.0555085
           },
           "start_address" : "115 Salem St, Boston, MA 02113, USA",
           "start_location" : {
              "lat" : 42.3644965,
              "lng" : -71.05552419999999
           },
           "steps" : [
              {
                 "distance" : {
                    "text" : "82 ft",
                    "value" : 25
                 },
                 "duration" : {
                    "text" : "1 min",
                    "value" : 11
                 },
                 "end_location" : {
                    "lat" : 42.3647252,
                    "lng" : -71.0555085
                 },
                 "html_instructions" : "Head \u003cb\u003enorth\u003c/b\u003e on \u003cb\u003eSalem St\u003c/b\u003e toward \u003cb\u003eJerusalem Pl\u003c/b\u003e",
                 "polyline" : {
                    "points" : "ciqaG~_upLO?]A"
                 },
                 "start_location" : {
                    "lat" : 42.3644965,
                    "lng" : -71.05552419999999
                 },
                 "travel_mode" : "DRIVING"
              }
           ],
           "traffic_speed_entry" : [],
           "via_waypoint" : []
        }
     ],
     "overview_polyline" : {
        "points" : "ciqaG~_upLm@A"
     },
     "summary" : "Salem St",
     "warnings" : [],
     "waypoint_order" : []
  }
  ],
"status" : "OK"
}

Декодируемая структура. Для работы с этим я использую декодируемую структуру. Мне удалось получить доступ к вложенным данным первого уровня (routes.summary), но я изо всех сил пытаюсь продвинуться дальше (например, route.legs.duration). Моя структура кода выглядит следующим образом:

struct Directions: Decodable {
    let status: String
    let routes: [Routes]

         enum CodingKeys :String, CodingKey {
              case status, routes
    }

struct Routes: Decodable {
    let summary: String
    let legs: [Legs]

         enum CodingKeys : String, CodingKey {
              case summary, legs
          }
}

struct Legs: Decodable {
    let duration: Duration

          enum CodingKeys : String, CodingKey {
          case duration
          }
    }

struct Duration: Decodable {
        let text: String    

            enum CodingKeys : String, CodingKey {
               case text
            }
        }

Реализация после настройки URL:

      URLSession.shared.dataTask(with: url) { (data, response, err) in
          guard let data = data else { return }
          do {   
             let directions = try
             JSONDecoder().decode(Directions.self, from: data)

        for item in directions.routes {
            self.stringoutput = item.summary          
       }

После всего этого все, что я хочу сделать, это получить доступ к «тексту» в JSON и вернуть это значение. Последняя строка кода может успешно вернуть «сводку» в формате JSON; и я могу распечатать (направления), и весь массив/словарь вернется в область отладки, включая «текст». Но я все еще не могу понять, как это сделать:

x = направления.маршруты.этапы.длительность.текст

сделать x равным «1 минуте»

Был бы признателен за чью-либо помощь.

Редактировать: в итоге сработали ключи структуры Vadian ниже и следующее for in loop:

                for item in directions.routes {
                    print(item.summary)
                    self.direct = item.summary
                    for items in item.legs {
                        self.stringoutput = items.duration.text
                        print(items.duration.text)
                        }

Ваше здоровье!


person Ospalding    schedule 12.12.2018    source источник
comment
урезать его бесполезно, как и этот нечитаемый беспорядок структур. Пожалуйста, отформатируйте код правильно и удалите ненужные пустые строки.   -  person vadian    schedule 13.12.2018
comment
Спасибо за отзыв о форматировании кода и обновлении JSON. Сделано несколько обновлений, которые, надеюсь, сделают его более читабельным — приносим извинения за ошибку новичка.   -  person Ospalding    schedule 13.12.2018
comment
Работает ли просто выполнение x = item.duration.text в цикле for?   -  person SirCxyrtyx    schedule 13.12.2018
comment
Этот x = directions.routes.legs.duration.text не будет работать, так как Legs является массивом. Так что вам придется сделать что-то вроде этого x = directions.routes.legs[0].duration.text.   -  person kathayatnk    schedule 13.12.2018
comment
Я бы порекомендовал вам поместить туда весь файл JSON, с которым вы ищете помощь. Если у вас есть конечная точка, которую вы можете раскрыть, которая отбрасывает JSON, я лично предпочитаю это, так как я могу сбросить ее в Postman и посмотреть, что вернется, своими глазами.   -  person Adrian    schedule 13.12.2018
comment
SirCxyrtyx - к сожалению, пытался, но выдает ошибку. Значение типа «Directions.Routes не имеет продолжительности члена.   -  person Ospalding    schedule 13.12.2018
comment
Kathayatnk - интересно, я играю с новым форматированием - к сожалению, продолжаю получать откаты Directions.Routes не имеет членских "ног". Не знаю, почему его нельзя найти.   -  person Ospalding    schedule 13.12.2018
comment
Адриан - добавил полный JSON, но не уверен, что понимаю, что вы имеете в виду: если у вас есть конечная точка, вы можете раскрыть, что отбрасывает JSON.   -  person Ospalding    schedule 13.12.2018
comment
Используйте этот онлайн-инструмент для преобразования JSON в Struct: json4swift.com Я использую только его. работает нормально   -  person Kathiresan Murugan    schedule 14.12.2018


Ответы (1)


Эти структуры не декодируют все ключи, но это отправная точка.

Если ключи и члены структуры имеют одно и то же имя, вам не нужно указывать CodingKeys

struct Directions: Decodable {
    let status: String
    let routes: [Route]
}

struct Route: Decodable {
    let summary: String
    let legs: [Leg]
}

struct Leg: Decodable {
    let duration : TextValue
    let distance : TextValue
    let endAddress : String
    let endLocation : Location
    let startAddress : String
    let startLocation : Location
    let steps : [Step]
}

struct TextValue: Decodable {
    let text: String
    let value : Int
}

struct Location: Decodable {
    let lat, lng : Double
}

struct Step: Decodable {
    let duration : TextValue
    let distance : TextValue
    let endLocation : Location
    let startLocation : Location
    let htmlInstructions : String
    let travelMode : String
}

Чтобы правильно декодировать ключи snake_cased, необходимо добавить соответствующую стратегию декодирования ключей.

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

Для доступа к массивам ([]) вам нужно получить элемент по индексу

step[0]

или перебрать массив с помощью цикла

for step in steps {}
person vadian    schedule 13.12.2018
comment
Вадиан, спасибо за помощь. В итоге я использовал ваш код, и для меня сработал цикл for in в моем отредактированном основном посте. Благодарю вас! - person Ospalding; 14.12.2018