简体   繁体   English

使用Codable解析嵌套的JSON数据

[英]Using Codable to parse nested JSON data

I'm trying to use Codable to parse JSON data. 我正在尝试使用Codable解析JSON数据。 But having some problems when it comes down to an object with arrays. 但是当归结为带有数组的对象时会遇到一些问题。 I've been trying to follow following answer , but i'm getting an error Type 'Feature' does not conform to protocol 'Encodable' 我一直在尝试遵循以下答案 ,但出现错误Type 'Feature' does not conform to protocol 'Encodable'

The JSON data I want are the latitude and longitude data, but i'm struggling to hard to learn Codable . 我想要的JSON数据是纬度和经度数据,但是我很难学习Codable I can also add that I tried to grab the id and it worked fine, but when i'm trying to go deeper, it just gives me an error. 我还可以补充一点,我尝试获取该id ,并且效果很好,但是当我尝试更深入时,它只会给我一个错误。

Any advice? 有什么建议吗? I do want to use Codable and not JSONSerialization . 我确实想使用Codable而不是JSONSerialization

My struct (So far) 我的结构(到目前为止)

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 JSON响应

{  
   "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"
         }
      }
   ]
}

The interested data 感兴趣的数据

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

The error the compiler is giving you is because your object doesn't conform to Encodable 编译器给您的错误是因为您的对象不符合Encodable

If you just need to go JSON -> object and not the other way around then you can use Decodable instead of Codable . 如果只需要使用Decodable > object而不是其他方式,则可以使用Decodable而不是Codable

Codable requires conformance to Encodable so you would also have to implement encode(to encoder: Encoder) Codable要求与Encodable Codable一致,因此您还必须实现Encodable encode(to encoder: Encoder)

After you fix that then you also need to fix your parsing of the nested containers. 修复该问题之后,还需要修复对嵌套容器的解析。

Your inner geometry object has different keys than your outer object so you need a separate CodingKey to pass. 内部几何对象与外部对象的键不同,因此需要单独的CodingKey进行传递。 You also need to go one level deeper than you currently are to get to your coordinates. 您还需要比当前深度更深一层才能到达坐标。

This version should work for the json in your question: 此版本适用于您问题中的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)
    }
}

First of all if you want only to decode JSON adopt only Decodable . 首先,如果只想解码JSON,则只能采用Decodable If you adopt Codable and write a custom initializer you have to write also an encoder method. 如果采用Codable并编写自定义初始化程序,则还必须编写编码器方法。 This is the message of the error. 这是错误消息。

I recommend to decode the JSON into separate structs. 我建议将JSON解码为单独的结构。 This requires much less code. 这需要更少的代码。 Write an extension of CLLocationCoordinate2D as a wrapper for the coordinates to adopt Decodable 编写一个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)
    }
}

The rest are only a few lines 剩下的只有几行

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

struct Feature: Decodable {
    let geometry : Geometry
}

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

You get the coordinates with 你得到的坐标

do {
    let result = try JSONDecoder().decode(Features.self, from: data)
    for feature in result.features {
        print(feature.geometry.coordinates)
    }
} catch { print(error) }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM