I have a GEOSwift feature like this:
{
"type" : "Feature",
"geometry" : {
"type" : "Point",
"coordinates" : [
-xx.xxxxxxxxxxxxxxx,
xx.xxxxxxxxxxxxxxx
]
},
"properties" : {
"mapLayer" : "MyMapLayer",
"data" : {
"id" : 42,
"sizeClass" : "Large",
// and so on...
},
"featureType" : "MyFeatureType"
}
}
I want to retrieve the data
member and put it into a struct that matches:
struct MyStruct: Decodable {
var id: Int
var sizeClass: String?
// and so on...
}
This code will get me the data
alone, but the datatype is GEOSwift.JSON, and I don't know how to stringify it to decode using the usual JSONDecoder class.
if case let .object(data) = feature.properties?["data"] {
// do stuff with data: GEOSwift.JSON to get it into MyStruct
}
Here is the GEOSwift.JSON enum:
import Foundation
public enum JSON: Hashable, Sendable {
case string(String)
case number(Double)
case boolean(Bool)
case array([JSON])
case object([String: JSON])
case null
/// Recursively unwraps and returns the associated value
public var untypedValue: Any {
switch self {
case let .string(string):
return string
case let .number(number):
return number
case let .boolean(boolean):
return boolean
case let .array(array):
return array.map { $0.untypedValue }
case let .object(object):
return object.mapValues { $0.untypedValue }
case .null:
return NSNull()
}
}
}
extension JSON: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .string(value)
}
}
extension JSON: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .number(Double(value))
}
}
extension JSON: ExpressibleByFloatLiteral {
public init(floatLiteral value: Double) {
self = .number(value)
}
}
extension JSON: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = .boolean(value)
}
}
extension JSON: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: JSON...) {
self = .array(elements)
}
}
extension JSON: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, JSON)...) {
let object = elements.reduce(into: [:]) { (result, element) in
result[element.0] = element.1
}
self = .object(object)
}
}
extension JSON: ExpressibleByNilLiteral {
public init(nilLiteral: ()) {
self = .null
}
}
You have to re encode feature.properties["data"] back to JSON, then decode it to MyStruct. You can't do this directly because GEOSwift.JSON doesn't conform to Decodable, but it does conform to Encodable, so you encode it then decode it back.
let myStructData = feature.properties?["data"] ?? nil
let jsonData = try! JSONEncoder().encode(myStructData)
let decoder = JSONDecoder()
myStruct = try decoder.decode(MyStruct.self, from: jsonData)