ios - Swift 5 解析多级 JSON
问题描述
我还在学习,所以任何帮助将不胜感激。我正在尝试解析 JSON 文件,但似乎无法使其工作。遵循了一些在线教程,但我找不到这种 json 树的教程。
这是json的结构:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "zone_restrictionate_uav.2120",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
24.98812963,
44.10877275
],
[
24.98806588,
44.1070722
],
[
24.98537796,
44.10717296
],
[
24.9854417,
44.10887351
],
[
24.98812963,
44.10877275
]
]
]
},
"geometry_name": "the_geom",
"properties": {
"zone_id": "RZ 2120",
"human_readable_definition": "POLYGON: 440631.5819N 0245917.2667E- 440625.4599N 0245917.0372E- 440625.8227N 0245907.3606E- 440631.9446N 0245907.5901E- 440631.5819N 0245917.2667E",
"wkt": "POLYGON((24.9881296281 44.1087727458,24.9880658794 44.1070721972,24.9853779561 44.1071729596,24.9854417047 44.1088735083,24.9881296281 44.1087727458))",
"lower_lim": "GND",
"upper_lim": "120m AGL",
"contact": "mailto: aerofoto@mapn.ro",
"status": "RESTRICTED"
}
},
{
"type": "Feature",
"id": "zone_restrictionate_uav.2121",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
26.44760265,
44.13453494
],
[
26.43440247,
44.11089351
],
[
26.38955244,
44.1359355
],
[
26.40275262,
44.15957693
],
[
26.44760265,
44.13453494
]
]
]
},
"geometry_name": "the_geom",
"properties": {
"zone_id": "RZ 2121",
"human_readable_definition": "POLYGON: 440804.3258N 0262651.3695E- 440639.2166N 0262603.8489E- 440809.3678N 0262322.3888E- 440934.4769N 0262409.9094E- 440804.3258N 0262651.3695E",
"wkt": "POLYGON((26.4476026463 44.1345349397,26.4344024672 44.1108935127,26.3895524427 44.1359355018,26.4027526218 44.1595769288,26.4476026463 44.1345349397))",
"lower_lim": "GND",
"upper_lim": "120m AGL",
"contact": "mailto: aerofoto@mapn.ro",
"status": "RESTRICTED"
}
}
],
"totalFeatures": 339,
"numberMatched": 339,
"numberReturned": 339,
"timeStamp": "2021-08-15T11:39:26.055Z"
}
我正在尝试从几何树级别获取坐标,以及从属性级别获取数据,我将使用它们来注释地图。但在那之前我只需要打印这些信息。
到目前为止,我有以下结构和函数:
struct Notam: Decodable {
let type: String
let features: [Feature]
let totalFeatures: Int
let numberMatched: Int
let numberReturned: Int
let timeStamp: String
}
struct Feature: Decodable {
let type: String
let id: String
let geometry: [Geometry]
let geometry_name: String
let properties: [Properties]
}
struct Geometry: Decodable {
let type: String
let coordinates: [[Double]]
}
struct Properties: Decodable {
let zone_id: String
let wkt: String
let contact: String
}
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello World")
.onAppear(perform: readFile)
}
private func readFile() {
if let url = URL(string: "https://www.yeltsy.com/wp-content/uploads/2021/08/restrictions.json"),
let data = try? Data(contentsOf: url) {
let decoder = JSONDecoder()
if let jsonData = try? decoder.decode(Notam.self, from: data) {
print(jsonData.totalFeatures)
}
}
}
}
如果我注释 Feature 结构的几何和属性常量,上面的代码就可以工作
更新 180821:
感谢@vadian 的评论,我发现了geoJson 解码器,因此如果有人需要,我将发布解析geoJson 并在地图上显示多边形叠加层的代码。
现在,我正在尝试找出当用户点击覆盖多边形时如何在地图上显示注释(检查下面的 decodeAnnotations() 函数)的解决方案。如果有带有位置的点注释会更容易。但我不知道如果它是一个多边形叠加层如何显示它们。如果有人有任何提示,请告诉我。
import SwiftUI
import MapKit
import CoreLocation
// All Map Data Goes here...
class MapViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var mapView = MKMapView()
@Published var region: MKCoordinateRegion!
// Based on Location it Will set up
// Alert
@Published var permissionDenied = false
@Published var mapType: MKMapType = .standard
func updateMapType() {
if mapType == .standard {
mapType = .hybrid
mapView.mapType = mapType
}
else {
mapType = .standard
mapView.mapType = mapType
}
}
// Focus Location...
func focusLocation() {
guard let _ = region else {return}
mapView.setRegion(region, animated: true)
mapView.setVisibleMapRect(mapView.visibleMapRect, animated: true)
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
// Checking User Permission for Location Sharing
switch manager.authorizationStatus {
case .denied:
// If request denied, alert the user ...
permissionDenied.toggle()
case .notDetermined:
//If not determined, Request permission
manager.requestWhenInUseAuthorization()
case .authorizedWhenInUse:
manager.requestLocation()
manager.startUpdatingLocation()
default:
()
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Getting user Region
guard let location = locations.last else {return}
self.region = MKCoordinateRegion(center: location.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
// Updating map
self.mapView.setRegion(self.region, animated: true)
//Smooth Animation..
self.mapView.setVisibleMapRect(self.mapView.visibleMapRect, animated: true)
manager.stopUpdatingLocation()
}
// Decode GeoJSON from the server
func showRestrictedZones(completion: @escaping ([MKOverlay]) -> ()) {
guard let url = URL(string: "https://www.yeltsy.com/wp-content/uploads/2021/08/restrictions.json") else {
fatalError("Unable to get geoJSON") }
downloadData(fromURL: url) { (returnedData) in
if let data = returnedData {
var geoJson = [MKGeoJSONObject]()
do {
geoJson = try MKGeoJSONDecoder().decode(data)
} catch {
fatalError("Unable to decode GeoJSON")
}
var overlays = [MKOverlay]()
for item in geoJson {
if let feature = item as? MKGeoJSONFeature {
for geo in feature.geometry {
if let polygon = geo as? MKPolygon {
overlays.append(polygon)
}
}
}
}
DispatchQueue.main.async {
completion(overlays)
}
}
}
}
func decodeAnnotations() {
guard let url = URL(string: "https://flightplan.romatsa.ro/init/static/zone_restrictionate_uav.json") else {
fatalError("Unable to get geoJSON") }
downloadData(fromURL: url) { (returnedData) in
if let jsonData = returnedData {
do {
let result = try JSONDecoder().decode(FeatureCollection.self, from: jsonData)
for feature in result.features {
print(feature.properties.contact)
}
} catch { print("Error while parsing: \(error)") }
}
}
}
func downloadData( fromURL url: URL, completion: @escaping (_ data: Data?) -> ()) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard
let data = data,
error == nil,
let response = response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300 else {
print("Error downloading data.")
completion(nil)
return
}
completion(data)
}
.resume()
}
}
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
@EnvironmentObject var mapData: MapViewModel
@State var restrictions: [MKOverlay] = []
func makeCoordinator() -> Coordinator {
return MapView.Coordinator()
}
func makeUIView(context: Context) -> MKMapView {
let view = mapData.mapView
view.showsUserLocation = true
view.delegate = context.coordinator
mapData.showRestrictedZones { (restrictions) in
self.restrictions = restrictions
view.addOverlays(self.restrictions)
}
return view
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
class Coordinator: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let polygon = overlay as? MKPolygon {
let renderer = MKPolygonRenderer(polygon: polygon)
renderer.fillColor = UIColor.purple.withAlphaComponent(0.2)
renderer.strokeColor = .purple.withAlphaComponent(0.7)
return renderer
}
return MKOverlayRenderer(overlay: overlay)
}
}
}
import SwiftUI
import CoreLocation
struct Home: View {
@StateObject var mapData = MapViewModel()
// Location Manager..
@State var locationManager = CLLocationManager()
var body: some View {
ZStack{
MapView()
.environmentObject(mapData)
.ignoresSafeArea(.all, edges: .all)
VStack {
Spacer()
VStack{
Button(action: mapData.focusLocation, label: {
Image(systemName: "location.fill")
.font(.title2)
.padding(10)
.background(Color.white)
})
Divider()
.frame(width: 20)
Button(action: mapData.updateMapType, label: {
Image(systemName: mapData.mapType == .standard ? "network" : "map.fill")
.font(.title2)
.padding(10)
.background(Color.white)
})
}
.background(Color.white)
.cornerRadius(10)
.frame(maxWidth: .infinity, alignment: .trailing)
.shadow(color: Color.black.opacity(0.2), radius: 5, x: 0, y: 5)
.padding()
}
}
.onAppear(perform: {
locationManager.delegate = mapData
locationManager.requestWhenInUseAuthorization()
})
.alert(isPresented: $mapData.permissionDenied, content: {
Alert(title: Text("Permission Denied"), message: Text("Please Enable Permission In App Settings"), dismissButton: .default(Text("Go To Settings"), action: {
// Redirecting user to the settings...
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
}))
})
}
}
解决方案
与您提供的 JSON 格式相比,您的结构中的字段类型有点不正确。这些结构对我有用:
struct Notam: Decodable {
let type: String
let features: [Feature]
let totalFeatures: Int
let numberMatched: Int
let numberReturned: Int
let timeStamp: String
}
struct Feature: Decodable {
let type: String
let id: String
let geometry: Geometry
let geometry_name: String
let properties: Properties
}
struct Geometry: Decodable {
let type: String
let coordinates: [[[Double]]]
}
struct Properties: Decodable {
let zone_id: String
let wkt: String
let contact: String
}
推荐阅读
- javascript - SQFLite 中的颤振持久化图像
- html - 如何在 jQuery 中进行 HTML 选择
- java - 为什么我可以用 Jackson 将 xml 文件中的反序列化类的标签完全拼错?
- r - R函数在第三种条件下将数据从一列复制到另一列
- lua - 两个具有共享字段的 Wireshark 解剖器
- google-sheets - 将行转换为列需要帮助
- automated-tests - 使用 Cypress 使用命令获取夹具数据
- android - 如何访问权限允许管理android studio中的所有文件
- c# - 具有类实现接口的 C# 方法泛型
- oracle - 如何允许来自 oracle 的 pyspark jdbc 的精度损失?