解析JSON的Swift问题:无法转换类型' __ NSCFDictionary'到了NSArray'错误

时间:2015-12-08 20:10:14

标签: ios json swift parsing

我对iOS开发很新,我在解析API的JSON响应方面遇到了问题。

以下是我的示例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
}

感谢。

2 个答案:

答案 0 :(得分: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,因为它支持更直观的可用初始化器。如果你想为一个类提供一个可用的初始化器,它需要你在失败之前初始化所有东西(看起来似乎反对我直截了当。)

答案 1 :(得分:0)

这是有效的JSON,完美转换为[String : AnyObject]

let string = "{\"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\"}}"

do{
  let dict = try NSJSONSerialization.JSONObjectWithData(string.dataUsingEncoding(NSUTF8StringEncoding)!, options:NSJSONReadingOptions.AllowFragments) as! [String : AnyObject]
  print(dict)
} catch let error as NSError{
  print(error.localizedDescription)
}