使用 Struct 的 Encodable 协议解码二维多边形坐标

Decode 2D Polygon coordinates using Struct's Encodable protocol

我正在尝试使用 Swift 结构的 Encodable 协议解析以下 json。 如果我将坐标设置为 AnyAnyObject,它会给出错误提示,说明不符合协议。

我知道这可以使用数组和字典来实现,但我不想那样做。

{
    "coordinates": [
        [
            [
                0.148271,
                51.6723432
            ],
            [
                0.148271,
                51.3849401
            ],
            [
                -0.3514683,
                51.3849401
            ],
            [
                -0.3514683,
                51.6723432
            ],
            [
                0.148271,
                51.6723432
            ]
        ]
    ]
} 

struct Geometry: Codable {
    let coordinates: [[[Double]]]

    init(from decoder: Decoder) throws {
        let data = try decoder.container(keyedBy: CodingKeys.self)
        coordinates = try data.decode([[[Double]]].self, forKey: .coordinates)
    }
}

do {
    let decoded = try JSONDecoder().decode(Geometry.self, from: data!)
    print(decoded)
    completionHandler(statusCode, decoded)
} catch {
    print("Failed to encode data.")
    completionHandler(statusCode, nil)
}

如何解决这个问题?

您在原始问题中提供的数据片段(在阅读我的评论后多次编辑之前)无效 JSON。 如果你把所有东西(你现在拥有的)都放在大括号中,它将是一个有效的 JSON 对象,带有 'coordinates' 字段。但是从您的(原始)问题来看,我了解到这可能不是您想要的。下一个有效的 JSON 是数组(即 'coordinates' 字段的内容。)

下面的代码片段给出了 JSON 对象:

import Foundation

let sample  = [[[0.148271, 51.6723432], [0.148272, 51.6723433], [0.148273, 51.6723434],]]

struct Geometry: Codable {
    var coordinates: [[[Double]]]

    init(coord: [[[Double]]]) {
        self.coordinates = coord
    }

////  Remove the comments below in order use a simple JSON array!
//    func encode(to encoder: Encoder) throws {
//        var container = encoder.singleValueContainer()
//        try container.encode(coordinates)
//    }
//
//    init(from decoder: Decoder) throws {
//        let container = try decoder.singleValueContainer()
//        coordinates = try container.decode([[[Double]]].self)
//    }
}

// Initialize a Geometry object
let geo = Geometry(coord: sample)

// Serialize into `data` and print the result
let data = try! JSONEncoder().encode(geo)
print (String(data: data, encoding: .utf8) ?? "-not representable-")

// Now, decode the data into a new Geometry instance `g` and print it
let g = try! JSONDecoder().decode(Geometry.self, from: data)
print (g)

这导致(JSON 格式化以提高可读性):

{
  "coordinates": [
    [
      [
        0.14827099999999999,
        51.6723432
      ],
      [
        0.14827199999999999,
        51.672343300000001
      ],
      [
        0.14827299999999999,
        51.672343400000003
      ]
    ]
  ]
}

Geometry(coordinates: [[[0.148271, 51.6723432], [0.148272, 51.6723433], [0.148273, 51.6723434]]])

如果您删除示例代码中的注释,您将得到没有大括号的数组,但也没有 "coordinates": 标签(JSON 格式化以提高可读性):

[
  [
    [
      0.14827099999999999,
      51.6723432
    ],
    [
      0.14827199999999999,
      51.672343300000001
    ],
    [
      0.14827299999999999,
      51.672343400000003
    ]
  ]
]

Geometry(coordinates: [[[0.148271, 51.6723432], [0.148272, 51.6723433], [0.148273, 51.6723434]]])

我的建议是让CLLocationCoordinate2D符合Codable解码Double

的数组
import CoreLocation

extension CLLocationCoordinate2D : Codable {
    public init(from decoder: Decoder) throws {
        var arrayContainer = try decoder.unkeyedContainer()
        if arrayContainer.count == 2 {
            let lat = try arrayContainer.decode(CLLocationDegrees.self)
            let lng = try arrayContainer.decode(CLLocationDegrees.self)
            self.init(latitude: lat, longitude: lng)
        } else {
            throw DecodingError.dataCorruptedError(in: arrayContainer, debugDescription: "Coordinate array must contain two items")
        }
    }

    public func encode(to encoder: Encoder) throws {
        var arrayContainer = encoder.unkeyedContainer()
        try arrayContainer.encode(contentsOf: [latitude, longitude])
    }
}

然后你可以简单地声明Geometry

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