json 如何在数据模型中将一个变量设置为多个类型

4dc9hkyq  于 2023-05-02  发布在  其他
关注(0)|答案(2)|浏览(96)

我的应用从API端点获取数据。
数据是一个Review,模型如下所示:

struct Review: Identifiable, Decodable {
    var id: Int
    var reviewable_type: String
    var score: Int?
    var reviewable: <THIS CAN BE A MOVIE, A SHOW OR AN EPISODE>
    var user: User
    var created_at: String?
    var created: String?
    var text: String?
}

正如您所看到的,reviewable属性可以有多种类型。我该如何实现这一点?我有不同的数据模型为电影,显示和插曲。注意:reviewable_type保存reviewable变量中的类型值。我不知道这有没有帮助。
我尝试了协议和扩展,但似乎无法让它工作。

2j4z5cfb

2j4z5cfb1#

你可以做的是将structReview转换成一个关联类型的协议,也就是另一个协议Reviewable

protocol Reviewable: Decodable {}

该协议必须至少符合Decodable协议,但也可以包含所有内容之间的公共属性,例如ID。
结构体Review如下转换:

protocol Review: Identifiable, Decodable {
    associatedtype Content: Reviewable
    
    var id: Int { get }
    var score: Int? { get }
    var reviewable: Content { get }
    var created_at: String? { get }
    var created: String? { get }
    var text: String? { get }
}

然后,您将为每种类型的可查看内容创建一个结构体:

struct Movie: Reviewable {}

struct Show: Reviewable {}

struct Episode: Reviewable {}

每个Review Type对应一个struct。

struct MovieReview: Review {
    var id: Int
    var score: Int?
    var reviewable: Movie
    var created_at: String?
    var created: String?
    var text: String?
}

struct ShowReview: Review {
    var id: Int
    var score: Int?
    var reviewable: Show
    var created_at: String?
    var created: String?
    var text: String?
}

struct EpisodeReview: Review {
    var id: Int
    var score: Int?
    var reviewable: Episode
    var created_at: String?
    var created: String?
    var text: String?
}

然后它们都可以在类型为any Review的数据结构中使用。下面是一个简单的例子:

let test: [any Review] = [
                            ShowReview(id: 0, reviewable: Show()),
                            MovieReview(id: 1, reviewable: Movie()),
                            EpisodeReview(id: 2, reviewable: Episode())
                         ]
vfhzx4xs

vfhzx4xs2#

我喜欢AntonioWar's answer,如果对象都有相同的字段,而它们没有。也许它仍然以这种方式工作,但从他的回答中我不清楚。
我解决这个问题的方法是:我创建了一个Reviewable结构体,其中包含ShowEpisodeMovie结构体中的所有字段:

struct Reviewable : Decodable {
    var id: Int
    var tmdb_id: Int?
    var name: String?
    var show_id: Int?
    var season: String?
    var number: String?
    var type: String?
    var airdate: String?
    var airtime: String?
    var airstamp: String?
    var average_rating: Float?
    var vote_average: String?
    var vote_count: String?
    var image_medium: String?
    var image_original: String?
    var summary: String?
    var watched_by_users: [User]?
    var show: Show?
    var comments_count: Int?
    var likes_count: Int?
    var episode_number: String?
    var new_airstamp: String?
    var backdrop_image: String?
    var poster_image: String?
    var likes: [Like]?
    var reviews: [Review]?
    var imdb_id: String?
    var adult: Int?
    var backdrop_path: String?
    var budget: String?
    var homepage: String?
    var original_language: String?
    var original_title: String?
    var tagline: String?
    var title: String?
    var overview: String?
    var popularity: String?
    var poster_path: String?
    var release_date: String?
    var trending: String?
    var revenue: String?
    var status: String?
    var movie_users: [MovieUser]?
    var tv_maze_id: Int?
    var tvrage_id: String?
    var thetvdb_id: String?
    var original_name: String?
    var language: String?
    var origin_country: String?
    var average_runtime: Int?
    var premiered: String?
    var schedule: String?
    var updated: String?
    var episodes: [Episode]?
    var tracked_by: [User]?
}

然后,我在Review结构体中添加了以下类型:

struct Review: Identifiable, Decodable {
    var id: Int
    var reviewable_type: String
    var score: Int?
    var reviewable: Reviewable
    var user: User
    var created_at: String?
    var created: String?
    var text: String?
}

然后,我为3个Reviewable对象创建了扩展,并使用一个函数将Reviewable转换为MovieEpisodeShow

extension Movie {
    static func fromReviewable(reviewable: Reviewable) -> Movie {
        let movie = Movie(
            id: reviewable.id,
            tmdb_id: reviewable.tmdb_id,
            imdb_id: reviewable.imdb_id,
            adult: reviewable.adult,
            backdrop_path: reviewable.backdrop_path,
            budget: reviewable.budget,
            homepage: reviewable.homepage,
            original_language: reviewable.original_language,
            original_title: reviewable.original_title,
            tagline: reviewable.tagline,
            title: reviewable.title,
            overview: reviewable.overview,
            popularity: reviewable.popularity,
            poster_path: reviewable.poster_path,
            release_date: reviewable.release_date,
            trending: reviewable.trending,
            revenue: reviewable.revenue,
            status: reviewable.status,
            vote_average: reviewable.vote_average,
            vote_count: reviewable.vote_count,
            likes: reviewable.likes,
            movie_users: reviewable.movie_users,
            reviews: reviewable.reviews,
            comments_count: reviewable.comments_count,
            likes_count: reviewable.likes_count
        )
        
        return movie
    }
}

因为我从Review上的reviewable_type变量知道它是哪种类型,所以我可以使用它将其解码为正确的结构:

if(review.reviewable_type == "App\\Models\\Movie") {
    let movie = Movie.fromReviewable(reviewable: review.reviewable!)
} else if(review.reviewable_type == "App\\Models\\Show") {
    let show = Show.fromReviewable(reviewable: review.reviewable!)
} else if(review.reviewable_type == "App\\Models\\Episode") {
    let episode = Episode.fromReviewable(reviewable: review.reviewable!)
}

也许这是一个非常破旧的方式来做,但这是什么为我工作(也许其他人可以使用它)。

相关问题