如何从 API 解析 JSON?

问题描述 投票:0回答:1

帮助我了解如何正确解析此类 JSON。我想不出正确的方法。这是 API 的链接。我需要在模型视图中指定什么?如果它是一个数组,那么他们的例子就很清楚了

@Published var viewModel: \[User\] = \[\]
。还是必须要提出一些条件?

网址https://run.mocky.io/v3/35e0d18e-2521-4f1b-a575-f0fe366f66e3

但如果这是这样的旋转

{
 "id": 1,
 "name": "Best five-star hotel in Hurghada, Egypt",
 "adress": "Madinat Makadi, Safaga Road, Makadi Bay, Egypt",
 "minimal_price": 134268,
 "price_for_it": "For the tour with flight",
 "rating": 5,
 "rating_name": "Excellent", 
 "image_urls": ["https://www.atorus.ru/sites/default/files/upload/image/News/56149/Club_Priv%C3%A9_by_Belek_Club_House.jpg", "https://deluxe.voyage/useruploads/articles/The_Makadi_Spa_Hotel_02.jpg", "https://deluxe.voyage/useruploads/articles/article_1eb0a64d00.jpg"],
 "about_the_hotel": {
    "description": "VIP-class hotel with its own golf courses. High level of service. Recommended for a respectable vacation. The hotel accepts guests from 18 years old!",
    "peculiarities": ["Free Wifi throughout the hotel", "1 km to the beach", "Free fitness club", "20 km to the airport"]
    
  }
}

我不明白如何得到正确答案。

这是模型

struct Hotel: Codable, Identifiable {
    let id: Int
    let name, adress: String
    let minimalPrice: Int
    let priceForIt: String
    let rating: Int
    let ratingName: String
    let imageUrls: [String]
    let aboutTheHotel: AboutTheHotel

    enum CodingKeys: String, CodingKey {
        case id, name, adress
        case minimalPrice = "minimal_price"
        case priceForIt = "price_for_it"
        case rating
        case ratingName = "rating_name"
        case imageUrls = "image_urls"
        case aboutTheHotel = "about_the_hotel"
    }
}

// MARK: - AboutTheHotel
struct AboutTheHotel: Codable {
    let description: String
    let peculiarities: [String]
}

这是视图模型

import Foundation
import SwiftUI
import Combine

class HotelViewModel: ObservableObject {
    
    @Published var hotelViewModel: [String: String] = [:]
    
    private var bag = Set<AnyCancellable>()
    
    func fetchHotel() {
        
        let urlString = "https://run.mocky.io/v3/35e0d18e-2521-4f1b-a575-f0fe366f66e3"
        
        guard let url = URL(string: urlString) else {
            print("🤬ERRRROOOOOOORRRRRR")
            return
        }
        
        URLSession
            .shared
            .dataTaskPublisher(for: url)
            .receive(on: DispatchQueue.main)
            .tryMap { res in
                guard let response = res.response as? HTTPURLResponse, response.statusCode >= 200 && response.statusCode < 300 else {
                    throw URLError(.badServerResponse)
                }
                
                let decoder = JSONDecoder()
                
                guard let hotel = try? decoder.decode(Hotel.self, from: res.data) else {
                    throw URLError(.badURL)
                }
                return hotel
            }
            .sink { completion in
                switch completion {
                case .finished:
                    print("AllGood")
                case .failure(let error):
                    print(error.localizedDescription)
                }
            } receiveValue: { [weak self] returnedHotel in
                self?.hotelViewModel = returnedHotel
            }
            .store(in: &bag)
    }
}

我不知道如何获得它。我找不到任何好的信息。

json swift url
1个回答
0
投票

您的网址仅返回一家酒店。您必须将其转换为酒店数组并将其分配给@Published var。例如:

class HotelViewModel: ObservableObject {
    
    @Published var hotels: [Hotel] = []
    @Published var modelError = false
    @Published var modelErrorMessage: String = ""

    func fetchHotel() {
        
        let urlString = "https://run.mocky.io/v3/35e0d18e-2521-4f1b-a575-f0fe366f66e3"
        
        guard let url = URL(string: urlString) else {
            print("🤬ERRRROOOOOOORRRRRR")
            return
        }

        
        URLSession
            .shared
            .dataTaskPublisher(for: url)
            .map(\.data) // Publish the received data
            .decode(type: Hotel.self, decoder: JSONDecoder()) // decode data
            .map({ hotel in
                [hotel]
            }) // transform hotel into an array
            .catch() { error -> Just<[Hotel]> in
                print("Error:\(error)")
                DispatchQueue.main.sync {
                    self.modelError = true
                    self.modelErrorMessage = "\(error)"
                }
                return Just(self.hotels)
            } // in case of error, return current hotel array
            .receive(on: DispatchQueue.main) // on main queue to refresh display
            .assign(to: &$hotels) // save into items
    }
}

使用示例:

struct HotelImageView: View {
    let imageUrl: String
    var body: some View {
            AsyncImage(url: URL(string: imageUrl)) { phase in
                switch phase {
                    case .failure:
                        Color.red
                            .frame(width: 256, height: 256) .clipShape(RoundedRectangle(cornerRadius: 25))
                    case .success(let image):
                        image
                            .resizable()
                            .frame(width: 256, height: 256) .clipShape(RoundedRectangle(cornerRadius: 25))
                    default:
                        ProgressView()
                            .frame(width: 256, height: 256) .clipShape(RoundedRectangle(cornerRadius: 25))
                }
            }
    }
}
        

struct HotelView: View {
    var hotel: Hotel
    var body: some View {
        VStack {
            Text("Hotel : " + hotel.name)
            VStack(alignment: .leading) {
                Text("Address : " + hotel.adress)
                Text("rating : \(hotel.rating)")
                Text("Price : \(hotel.minimalPrice)")
                LazyVGrid(columns: [GridItem(), GridItem()]){
                    ForEach(hotel.imageUrls, id:\.self) { imageUrl in
                        HotelImageView(imageUrl: imageUrl)
                    }
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                }
            }
            .padding([.leading], 20)
        }
    }
}

struct ContentView: View {
    @StateObject var hotelModel = HotelViewModel()
    
    var body: some View {
        VStack {
            List {
                Section {
                    ForEach(hotelModel.hotels) { hotel in
                        HotelView(hotel: hotel)
                    }
                } header: {
                    Text("Hotels")
                } footer: {
                    if self.hotelModel.modelError {
                        Text("Error \(self.hotelModel.modelErrorMessage)")
                    } else {
                        Text("Found \(self.hotelModel.hotels.count)")
                    }
                }

            }
        }
        .onAppear() {
            self.hotelModel.fetchHotel()
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.