2023-06-06

Convert GEOSwift.JSON to struct in Swift

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
    }
}


No comments:

Post a Comment