Использование Codable для анализа вложенных данных JSON - PullRequest
0 голосов
/ 27 апреля 2018

Я пытаюсь использовать Codable для анализа данных JSON. Но возникают некоторые проблемы, когда дело доходит до объекта с массивами. Я пытался следовать следующим ответом , но я получаю сообщение об ошибке Type 'Feature' does not conform to protocol 'Encodable'

Данные JSON, которые мне нужны, - это данные о широте и долготе, но я изо всех сил пытаюсь усвоить Codable. Я также могу добавить, что я пытался схватить id, и он работал нормально, но когда я пытаюсь пойти глубже, это просто дает мне ошибку.

Любой совет? Я хочу использовать Codable, а не JSONSerialization.

Моя структура (пока)

struct Features: Codable {
    var features: [Feature]
}

struct Feature: Codable {
    var lat: Double
    var long: Double


    enum CodingKeys: String, CodingKey {
        case geometry
    }


    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let geometry = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .geometry)
        var coordinates = try geometry.nestedUnkeyedContainer(forKey: .geometry)
        long = try coordinates.decode(Double.self)
        lat = try coordinates.decode(Double.self)

    }
}

JSON RESPONSE

{  
   "type":"FeatureCollection",
   "totalFeatures":1761,
   "features":[  
      {  
         "type":"Feature",
         "id":"LTFR_P_RORELSEHINDRADE.3179814",
         "geometry":{  
            "type":"LineString",
            "coordinates":[  
               [  
                  17.929374,
                  59.387507
               ],
               [  
                  17.929364,
                  59.387493
               ]
            ]
         },
         "geometry_name":"GEOMETRY",
         "properties":{  
            "FID":3179814,
            "FEATURE_OBJECT_ID":2406812,
            "FEATURE_VERSION_ID":1,
            "EXTENT_NO":2,
            "VALID_FROM":"2008-10-09T22:00:00Z",
            "CITATION":"0180 2008-09122",
            "STREET_NAME":"Visbyringen",
            "CITY_DISTRICT":"Rinkeby",
            "PARKING_DISTRICT":"<Område saknas>",
            "ADDRESS":"Visbyringen 4",
            "VF_METER":12,
            "VF_PLATS_TYP":"Reserverad p-plats rörelsehindrad",
            "RDT_URL":"https://rdt.transportstyrelsen.se/rdt/AF06_View.aspx?BeslutsMyndighetKod=0180&BeslutadAr=2008&LopNr=09122"
         }
      }
   ]
}

Интересующие данные

"coordinates":[  
   [  
      17.929374,
      59.387507
   ],
   [  
      17.929364,
      59.387493
   ]
]

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Прежде всего, если вы хотите только декодировать JSON, используйте только Decodable. Если вы принимаете Codable и пишете пользовательский инициализатор, вы должны также написать метод кодировщика. Это сообщение об ошибке.

Я рекомендую декодировать JSON в отдельные структуры. Это требует гораздо меньше кода. Напишите расширение CLLocationCoordinate2D в качестве оболочки для координат, которые нужно принять Decodable

import CoreLocation

extension CLLocationCoordinate2D : Decodable {
    public init(from decoder: Decoder) throws {
        var arrayContainer = try decoder.unkeyedContainer()
        let lat = try arrayContainer.decode(CLLocationDegrees.self)
        let lng = try arrayContainer.decode(CLLocationDegrees.self)
        self.init(latitude: lat, longitude: lng)
    }
}

Остальные всего несколько строк

struct Features: Decodable {
    var features: [Feature]
}

struct Feature: Decodable {
    let geometry : Geometry
}

struct Geometry: Decodable {
    let coordinates : [CLLocationCoordinate2D]
}

Вы получаете координаты с

do {
    let result = try JSONDecoder().decode(Features.self, from: data)
    for feature in result.features {
        print(feature.geometry.coordinates)
    }
} catch { print(error) }
0 голосов
/ 27 апреля 2018

Ошибка компилятора в том, что ваш объект не соответствует Encodable

Если вам просто нужно перейти в JSON -> объект, а не наоборот, тогда вы можете использовать Decodable вместо Codable.

Codable требует соответствия Encodable, поэтому вам также придется реализовать encode(to encoder: Encoder)

После того, как вы исправите это, вам также понадобится исправить ваш анализ вложенных контейнеров.

Ваш внутренний объект геометрии имеет ключи, отличные от вашего внешнего объекта, поэтому для прохождения вам нужен отдельный CodingKey. Вам также нужно пройти на один уровень глубже, чем вы сейчас находитесь, чтобы добраться до своих координат.

Эта версия должна работать для json в вашем вопросе:

struct Features: Decodable {
    var features: [Feature]
}

struct Feature: Decodable {
    var lat: Double
    var long: Double

    enum CodingKeys: String, CodingKey {
        case geometry
    }

    enum GeometryKeys: String, CodingKey {
        case coordinates
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let geometry = try values.nestedContainer(keyedBy: GeometryKeys.self, forKey: .geometry)
        var coordinates = try geometry.nestedUnkeyedContainer(forKey: .coordinates)

        var longLat = try coordinates.nestedUnkeyedContainer()
        long = try longLat.decode(Double.self)
        lat = try longLat.decode(Double.self)
    }
}
...