我想使用這個 Pokémon API來獲取一些資料并將其轉換為 SwiftPokemon
結構。
這是我在獲取 Pokemon #142 時得到的回應的摘錄:
{
"id": 142,
"name": "aerodactyl",
"types": [{
"type": {
"name": "rock",
"url": "https://pokeapi.co/api/v2/type/6/"
},
"slot": 1
},
{
"type": {
"name": "flying",
"url": "https://pokeapi.co/api/v2/type/3/"
},
"slot": 2
}
]
}
這是我撰寫的用于將此 JSON 轉換為 Swift 型別的結構:
struct Pokemon: Codable {
var id: Int
let name: String
var types: [PokemonType]?
}
struct PokemonType: Codable {
var type: PokemonTypeContent
}
struct PokemonTypeContent: Codable {
var name: PokemonTypeNameContent
}
enum PokemonTypeNameContent: String, Codable {
case flying = "flying"
case rock = "rock"
// ...
}
現在這是我的問題:當我想獲得口袋妖怪型別時,我需要深入研究:
pokemon.types.first?.type.name
我想知道我是否有一種方法可以PokemonTypeNameContent
在結構中獲取陣列Pokemon
,以執行以下操作:
struct Pokemon {
var types: [PokemonTypeNameContent]?
}
(我對獲取slot
值不感興趣)。
感謝您的幫助!
uj5u.com熱心網友回復:
您可以對 進行自定義編碼PokemonTypeNameContent
,并使用 JSON 遍歷各個級別nestedContainer
enum PokemonTypeNameContent: String, Decodable {
case flying = "flying"
case rock = "rock"
// ...
enum OuterCodingKeys: CodingKey { case type }
enum InnerCodingKeys: CodingKey { case name }
init(from decoder: Decoder) throws {
// this is the container for each JSON object in the "types" array
let container = try decoder.container(keyedBy: OuterCodingKeys.self)
// this finds the nested container (i.e. JSON object) associated with the key "type"
let innerContainer = try container.nestedContainer(keyedBy: InnerCodingKeys.self, forKey: .type)
// now we can decode "name" as a string
let name = try innerContainer.decode(String.self, forKey: .name)
if let pokemonType = Self.init(rawValue: name) {
self = pokemonType
} else {
throw DecodingError.typeMismatch(
PokemonTypeNameContent.self,
.init(codingPath: innerContainer.codingPath [InnerCodingKeys.name],
debugDescription: "Unknown pokemon type '\(name)'",
underlyingError: nil
)
)
}
}
}
// Pokemon can then be declared like this:
struct Pokemon: Decodable {
let id: Int
let name: String
let types: [PokemonTypeNameContent]
}
請注意,這意味著您失去了將解碼PokemonTypeNameContent
作為常規列舉的選項。如果您確實想這樣做,請將自定義解碼代碼放入屬性包裝器中。請注意,我們將解碼整個 JSON 陣列,而不是每個 JSON 物件。
@propertyWrapper
struct DecodePokemonTypes: Decodable {
var wrappedValue: [PokemonTypeNameContent]
init(wrappedValue: [PokemonTypeNameContent]) {
self.wrappedValue = wrappedValue
}
enum OuterCodingKeys: CodingKey { case type }
enum InnerCodingKeys: CodingKey { case name }
init(from decoder: Decoder) throws {
// container for the "types" JSON array
var unkeyedContainer = try decoder.unkeyedContainer()
wrappedValue = []
// while we are not at the end of the JSON array
while !unkeyedContainer.isAtEnd {
// similar to the first code snippet
let container = try unkeyedContainer.nestedContainer(keyedBy: OuterCodingKeys.self)
let innerContainer = try container.nestedContainer(keyedBy: InnerCodingKeys.self, forKey: .type)
let name = try innerContainer.decode(String.self, forKey: .name)
if let pokemonType = PokemonTypeNameContent(rawValue: name) {
wrappedValue.append(pokemonType)
} else {
throw DecodingError.typeMismatch(
PokemonTypeNameContent.self,
.init(codingPath: innerContainer.codingPath [InnerCodingKeys.name],
debugDescription: "Unknown pokemon type '\(name)'",
underlyingError: nil
)
)
}
}
}
}
// You would write this in Pokemon
@DecodePokemonTypes
var types: [PokemonTypeNameContent]
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/471653.html