Swift 解析 JSON 的问题︰ 不能赶出 '__NSCFDictionary' 到 'NSArray' 错误类型的值

标签: json ios Swift
发布时间: 2017/3/5 16:43:09
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.

我是很新的 iOS 开发还有一个解析 JSON 响应从 API 的问题。

这里是我的样品 JSON 的样子︰

{
"recipe":{
"publisher":"Real Simple",
"f2f_url":"http://food2fork.com/view/39999",
"ingredients":[
    "1 tablespoon olive oil",
    "1 red onion, chopped",
    "2 small yellow squash, cut into 1/2-inch pieces",
    "2 cloves garlic, chopped",
    "1 jalapeo, seeded and thinly sliced",
    "1 kosher salt and black pepper",
    "4 28-ounce can diced tomatoes\n"
],
"source_url":"http://www.realsimple.com/food-recipes/browse-all-recipes/halibut-spicy-squash-tomatoes-00000000006842/index.html",   
"recipe_id":"39999", "image_url":"http://static.food2fork.com/someurl.jpg",
  "social_rank":95.14721536803285,
  "publisher_url":"http://realsimple.com",
  "title":"Halibut With Spicy Squash and Tomatoes"
  }
}

和打印 JSON (在此示例中的另一个) 时,它看起来像这样︰

["recipe": {
    "f2f_url" = "http://food2fork.com/view/20970";
    "image_url" = "http://static.food2fork.com/98113574b0.jpg";
    ingredients =     (
    "1 (170 gram) can crabmeat",
    "125 grams PHILADELPHIA Light Brick Cream Cheese Spread, softened",
    "2 green onions, thinly sliced",
    "1/4 cup MIRACLE WHIP Calorie-Wise Dressing",
    "12 wonton wrappers"
  );
    publisher = "All Recipes";
    "publisher_url" = "http://allrecipes.com";
    "recipe_id" = 20970;
    "social_rank" = "41.83825995815504";
    "source_url" = "http://allrecipes.com/Recipe/Philly-Baked-Crab-Rangoon/Detail.aspx";
    title = "PHILLY Baked Crab Rangoon";
}]

有一个物体,食谱和它看起来像这样︰

class Recipe {
  struct Keys {
      static let Title = "title"
      static let ImageUrl = "image_url"
      static let Ingredients = "ingredients"
      static let RecipeId = "recipe_id"
  }

  var title : String? = nil
  var id = 0
  var imageUrl : String? = nil
  var ingredients : String? = nil

  init(dictionary : NSDictionary) {
      self.title = dictionary[Keys.Title] as? String
      self.id = dictionary[RecipeDB.Keys.ID] as! Int
      self.imageUrl = dictionary[Keys.ImageUrl] as? String
      self.ingredients = dictionary[Keys.Ingredients] as? String
    }
}

当我尝试解析 JSON 并将其转换为我得到一本字典和 Could not cast value of type '__NSCFDictionary' to 'NSArray' 错误

这里是我投一个字典和原因的错误响应的方法

func recipiesFromData(data: NSData) -> [Recipe] {

    var dictionary : [String : AnyObject]!

    dictionary = (try! NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)) 
  as! [String : AnyObject]

    let recipeDictionaries = dictionary["recipe"] as! [[String : AnyObject]]

    let recipies = recipeDictionaries.map() { Recipe(dictionary: $0) }

    return recipies
}

谢谢你。

解决方法 1:

如果这是你的 JSON (单一),解析代码会看起来像︰

func recipeFromData(data: NSData) -> Recipe {
    let dictionary = (try! NSJSONSerialization.JSONObjectWithData(data, options: [])) as! [String : [String : AnyObject]]

    return Recipe(dictionary: dictionary["recipe"]!)
}

我可能调整 Recipe 类像这样︰

class Recipe {
    struct Keys {
        static let Title = "title"
        static let ImageUrl = "image_url"
        static let Ingredients = "ingredients"
        static let RecipeId = "recipe_id"
    }

    var title: String?
    var id: Int
    var imageUrl: String?
    var ingredients: [String]?

    init(dictionary : [String : AnyObject]) {
        self.title = dictionary[Keys.Title] as? String
        self.id = Int(dictionary[Keys.RecipeId] as! String)!
        self.imageUrl = dictionary[Keys.ImageUrl] as? String
        self.ingredients = dictionary[Keys.Ingredients] as? [String]
    }
}

应解析 JSON。


就个人而言,我会删除所有的那些 ! 因为如果有什么错误,就会导致崩溃。例如︰

enum RecipeError: ErrorType {
    case InvalidJSON(message: String, userInfo: [NSObject: AnyObject])
    case MalformedJSON
    case RecipeKeyNotFound
    case BadKeysValues
}

func recipeFromData(data: NSData) throws -> Recipe {
    var jsonObject: AnyObject

    do {
        jsonObject = try NSJSONSerialization.JSONObjectWithData(data, options: [])
    } catch let parseError as NSError {
        throw RecipeError.InvalidJSON(message: parseError.localizedDescription, userInfo: parseError.userInfo)
    }

    guard let dictionary = jsonObject as? [String : AnyObject] else {
        throw RecipeError.MalformedJSON
    }

    guard let recipeDictionary = dictionary["recipe"] as? [String: AnyObject] else {
        throw RecipeError.RecipeKeyNotFound
    }

    guard let recipe = Recipe(dictionary: recipeDictionary) else {
        throw RecipeError.BadKeysValues
    }

    return recipe
}

你是否去这种极端的就是什么样的错误你要能够捕捉到优雅,但希望这说明了这一点,你想要避免使用强迫展开 (与 !as! ) 如果你要从远程数据源,可能会引入您的应用程序必须预见和优雅地处理,而不是只崩溃的问题的数据处理方法。

顺便说一句,在上面的示例中,给 Recipe 与初始值设定项︰

struct Recipe {
    struct Keys {
        static let Title = "title"
        static let ImageUrl = "image_url"
        static let Ingredients = "ingredients"
        static let RecipeId = "recipe_id"
    }

    let title: String?
    let id: Int
    let imageUrl: String?
    let ingredients: [String]?

    init?(dictionary : [String : AnyObject]) {
        if let idString = dictionary[Keys.RecipeId] as? String, let id = Int(idString) {
            self.id = id
        } else {
            return nil
        }
        self.title = dictionary[Keys.Title] as? String
        self.imageUrl = dictionary[Keys.ImageUrl] as? String
        self.ingredients = dictionary[Keys.Ingredients] as? [String]
    }
}

(请注意,我已经做了这个 struct ,因为它支持更直观与初始值设定项。如果你想与初始值设定项为一类,它需要你初始化一切之前你失败 (这似乎是背道而驰直观给我)。

赞助商