使用Codable解析嵌套的JSON数据

时间:2018-04-27 15:15:31

标签: ios json swift codable

我尝试使用Codable来解析JSON数据。但是当涉及到具有数组的对象时会遇到一些问题。我一直在尝试关注answer,但我收到了错误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 :(得分:2)

编译器给出的错误是因为您的对象不符合Encodable

如果你只需要去JSON - &gt;对象而不是相反,您可以使用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)
    }
}

答案 1 :(得分:1)

首先,如果您只想解码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) }