如何在 Swift 中使用带有递增键的自定义解码器初始化来解析 JSON
How to parse JSON using custom decoder init with incrementing keys in Swift
我吃了一顿饭 json 我想将其转换成 MealData
结构
let randomMealJson = """
{
"meals": [{
"idMeal": "52812",
"strMeal": "Beef Brisket Pot Roast",
"strDrinkAlternate": null,
"strCategory": "Beef",
"strArea": "American",
"strInstructions": "Meal instructions",
"strMealThumb": "https://www.themealdb.com/images/media/meals/ursuup1487348423.jpg",
"strTags": "Meat",
"strYoutube": "https://www.youtube.com/watch?v=gh48wM6bPWQ",
"strIngredient1": "Beef Brisket",
"strIngredient2": "Salt",
"strIngredient3": "Onion",
"strIngredient4": "Garlic",
"strIngredient5": "Thyme",
"strIngredient6": "Rosemary",
"strIngredient7": "Bay Leaves",
"strIngredient8": "beef stock",
"strIngredient9": "Carrots",
"strIngredient10": "Mustard",
"strIngredient11": "Potatoes",
"strIngredient12": null,
"strIngredient13": null,
"strIngredient14": null,
"strIngredient15": null,
"strIngredient16": null,
"strIngredient17": null,
"strIngredient18": null,
"strIngredient19": null,
"strIngredient20": null,
"strMeasure1": "4-5 pound",
"strMeasure2": "Dash",
"strMeasure3": "3",
"strMeasure4": "5 cloves",
"strMeasure5": "1 Sprig",
"strMeasure6": "1 sprig ",
"strMeasure7": "4",
"strMeasure8": "2 cups",
"strMeasure9": "3 Large",
"strMeasure10": "1 Tbsp",
"strMeasure11": "4 Mashed",
"strMeasure12": "",
"strMeasure13": "",
"strMeasure14": "",
"strMeasure15": "",
"strMeasure16": "",
"strMeasure17": "",
"strMeasure18": "",
"strMeasure19": "",
"strMeasure20": "",
"strSource": "http://www.simplyrecipes.com/recipes/beef_brisket_pot_roast/",
"dateModified": null
}]
}
"""
这是我要制作的结构...
struct MealData: Decodable {
let meals: [Meal]
}
struct Meal: Decodable {
let items: [Item]
}
extension Meal {
struct Item: Decodable {
let ingredient: String
let measure: String
}
}
extension Meal {
init(from decoder: Decoder) throws {
// var items: [Item] = (0...20).map { num in
// what to do here?
// }
}
}
我想仔细研究成分和措施,并从中创建一个 Meal.Item
,但我不知道如何实现。可以按照我在代码中指出的方式吗?
我会分两步完成。首先解码成字典
struct MealData: Decodable {
let meals: [[String: String?]]
}
然后我会在单独的步骤中转换为另一种类型
struct Recipie {
var ingredients: [Ingredient]
}
struct Ingredient {
let ingredient: String
let measure: String
}
这是完整的代码
do {
let result = try JSONDecoder().decode(MealData.self, from: randomMealJson)
var recipies = [Recipie]()
for meal in result.meals {
var recipie = Recipie(ingredients: [])
var index = 1
while true {
guard let ingredientValue = meal["strIngredient\(index)"],
let measureValue = meal["strMeasure\(index)"],
let ingredient = ingredientValue,
let measure = measureValue, !measure.isEmpty
else { break }
recipie.ingredients.append(Ingredient(ingredient: ingredient, measure: measure))
recipies.append(recipie)
index += 1
}
}
} catch {
print(error)
}
关注@joakim-danielson的回答。您可以在 init(from:)
初始值设定项中做同样的事情,就像您的初衷一样。
只需将每个 Meal
的成分和尺寸解码为 [String: String?]
并编写您的自定义 Item
s:
extension Meal {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let mealDict = try container.decode([String: String?].self)
var index = 1
var items = [Item]()
while
let ingredient = mealDict["strIngredient\(index)"] as? String,
let measure = mealDict["strMeasure\(index)"] as? String,
!measure.isEmpty
{
items.append(Item(ingredient: ingredient, measure: measure))
index += 1
}
self.items = items
}
}
我吃了一顿饭 json 我想将其转换成 MealData
结构
let randomMealJson = """
{
"meals": [{
"idMeal": "52812",
"strMeal": "Beef Brisket Pot Roast",
"strDrinkAlternate": null,
"strCategory": "Beef",
"strArea": "American",
"strInstructions": "Meal instructions",
"strMealThumb": "https://www.themealdb.com/images/media/meals/ursuup1487348423.jpg",
"strTags": "Meat",
"strYoutube": "https://www.youtube.com/watch?v=gh48wM6bPWQ",
"strIngredient1": "Beef Brisket",
"strIngredient2": "Salt",
"strIngredient3": "Onion",
"strIngredient4": "Garlic",
"strIngredient5": "Thyme",
"strIngredient6": "Rosemary",
"strIngredient7": "Bay Leaves",
"strIngredient8": "beef stock",
"strIngredient9": "Carrots",
"strIngredient10": "Mustard",
"strIngredient11": "Potatoes",
"strIngredient12": null,
"strIngredient13": null,
"strIngredient14": null,
"strIngredient15": null,
"strIngredient16": null,
"strIngredient17": null,
"strIngredient18": null,
"strIngredient19": null,
"strIngredient20": null,
"strMeasure1": "4-5 pound",
"strMeasure2": "Dash",
"strMeasure3": "3",
"strMeasure4": "5 cloves",
"strMeasure5": "1 Sprig",
"strMeasure6": "1 sprig ",
"strMeasure7": "4",
"strMeasure8": "2 cups",
"strMeasure9": "3 Large",
"strMeasure10": "1 Tbsp",
"strMeasure11": "4 Mashed",
"strMeasure12": "",
"strMeasure13": "",
"strMeasure14": "",
"strMeasure15": "",
"strMeasure16": "",
"strMeasure17": "",
"strMeasure18": "",
"strMeasure19": "",
"strMeasure20": "",
"strSource": "http://www.simplyrecipes.com/recipes/beef_brisket_pot_roast/",
"dateModified": null
}]
}
"""
这是我要制作的结构...
struct MealData: Decodable {
let meals: [Meal]
}
struct Meal: Decodable {
let items: [Item]
}
extension Meal {
struct Item: Decodable {
let ingredient: String
let measure: String
}
}
extension Meal {
init(from decoder: Decoder) throws {
// var items: [Item] = (0...20).map { num in
// what to do here?
// }
}
}
我想仔细研究成分和措施,并从中创建一个 Meal.Item
,但我不知道如何实现。可以按照我在代码中指出的方式吗?
我会分两步完成。首先解码成字典
struct MealData: Decodable {
let meals: [[String: String?]]
}
然后我会在单独的步骤中转换为另一种类型
struct Recipie {
var ingredients: [Ingredient]
}
struct Ingredient {
let ingredient: String
let measure: String
}
这是完整的代码
do {
let result = try JSONDecoder().decode(MealData.self, from: randomMealJson)
var recipies = [Recipie]()
for meal in result.meals {
var recipie = Recipie(ingredients: [])
var index = 1
while true {
guard let ingredientValue = meal["strIngredient\(index)"],
let measureValue = meal["strMeasure\(index)"],
let ingredient = ingredientValue,
let measure = measureValue, !measure.isEmpty
else { break }
recipie.ingredients.append(Ingredient(ingredient: ingredient, measure: measure))
recipies.append(recipie)
index += 1
}
}
} catch {
print(error)
}
关注@joakim-danielson的回答。您可以在 init(from:)
初始值设定项中做同样的事情,就像您的初衷一样。
只需将每个 Meal
的成分和尺寸解码为 [String: String?]
并编写您的自定义 Item
s:
extension Meal {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let mealDict = try container.decode([String: String?].self)
var index = 1
var items = [Item]()
while
let ingredient = mealDict["strIngredient\(index)"] as? String,
let measure = mealDict["strMeasure\(index)"] as? String,
!measure.isEmpty
{
items.append(Item(ingredient: ingredient, measure: measure))
index += 1
}
self.items = items
}
}