使用 Codable 解析嵌套的 JSON 数据

Posted

技术标签:

【中文标题】使用 Codable 解析嵌套的 JSON 数据【英文标题】:Using Codable to parse nested JSON data 【发布时间】:2018-04-27 15:15:31 【问题描述】:

我正在尝试使用 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 响应

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

【问题讨论】:

它会给你什么错误? 如上所述:Type 'Feature' does not conform to protocol 'Encodable@Andy 【参考方案1】:

编译器给你的错误是因为你的对象不符合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)
    

【讨论】:

它运行良好!非常感谢你!我会把它标记为答案! 我有另一个关于这个的问题。通过使用上面的代码,我只得到坐标数组的第一个对象。我试图制作另一个结构,但这一切都给了我 nil 的价值 看看@vadian 的回答。它将所有坐标拉成一个数组 是的,我试过了,但是当我必须获取更多信息(例如属性值 + 坐标)时,它似乎不起作用。我会尝试使用 vadian 所说的新结构,因为坐标在数组中的数组中。【参考方案2】:

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

【讨论】:

您是否有将模型用作可编码模型的示例,我们可以将模型用于核心数据保存模型和 json 解析模型?我不想为代码数据实体和单独的模型对象创建自定义子类。我想为两者使用一 (1) 个模型类,它可以子类化可编码和 nsmanagedobject 或任何东西。在我们的旧项目中,我们使用了 2 个模型。在转换之前,我们使用 mapper 方法将 nsmanageobejct 转换为 nsobject(核心数据获取)和 nsobject 转换为 nsmanagedobejct(核心数据保存)。有几天我一直在为这样的例子而苦苦挣扎:(

以上是关于使用 Codable 解析嵌套的 JSON 数据的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Codable 和 Swift 解析这个嵌套的 JSON?

iOS swift Codable 不能与 Alamofire 一起使用 JSON 嵌套数据?

使用 URLSession 和 Codable 解析 JSON 数据

使用 Codable 解析 JSON 数据

JSON 解析,嵌套 JSON 结构的问题

使用 Codable 解析多级 JSON