在 Swift 中解析 json,AnyObject 类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24671249/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 17:24:57  来源:igfitidea点击:

Parse json in Swift, AnyObject type

jsonparsingswift

提问by alpennec

I'm trying to parse a json but I have some difficulties with the data types and notably the AnyObject type + downcasting.

我正在尝试解析 json,但我在数据类型方面遇到了一些困难,尤其是 AnyObject 类型 + 向下转换。

Let's consider the following json (it's an extract of a full json).

让我们考虑以下 json(它是完整 json 的摘录)。

{  "weather":
   [
      {
         "id":804,
         "main":"Clouds",
         "description":"overcast clouds",
         "icon":"04d"
      }
   ],
}

To me, the json can be described as follow :

对我来说,json 可以描述如下:

- json: Dictionary of type [String: AnyObject] (or NSDictionary, so = [NSObject, AnyObject] in Xcode 6 b3)
    - "weather": Array of type [AnyObject] (or NSArray)
         - Dictionary of type [String: AnyObject] (or NSDictionary, so = [NSObject, AnyObject] in Xcode 6 b3)

My json is of type AnyObject! (I use JSONObjectWithDatato get the JSON from a URL).

我的 json 是 AnyObject 类型的!(我用来JSONObjectWithData从 URL 获取 JSON)。

I then want to access the weather Dictionary. Here is the code I wrote.

然后我想访问天气词典。这是我写的代码。

var localError: NSError?
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &localError)

if let dict = json as? [String: AnyObject] {
 if let weatherDictionary = dict["weather"] as? [AnyObject] {
      // Do stuff with the weatherDictionary
    }
}

Here is the error I got

这是我得到的错误

Playground execution failed: error: <EXPR>:28:56: error: '[AnyObject]' is not a subtype of '(String, AnyObject)'
        if let weatherDictionary = dict["weather"] as? [AnyObject] {

I don't understand why dict["weather"] is compared to a subtype of (String, AnyObject) and not AnyObject.

我不明白为什么将 dict["weather"] 与 (String, AnyObject) 而不是 AnyObject 的子类型进行比较。

I declared my dictionary as [String: AnyObject], so I i access a value using the String key, I should have an AnyObject, no ?

我将我的字典声明为 [String: AnyObject],所以我使用 String 键访问一个值,我应该有一个 AnyObject,不是吗?

If I use NSDictionary instead of [String: AnyObject], it works.

如果我使用 NSDictionary 而不是 [String: AnyObject],它会起作用。

If I use NSArray instead of [AnyObject], it works.

如果我使用 NSArray 而不是 [AnyObject],它会起作用。

- The Xcode 6 beta 3 release notes tell that "NSDictionary* is now imported from Objective-C APIs as [NSObject : AnyObject].".
- And the Swift book: "When you bridge from an NSArray object to a Swift array, the resulting array is of type [AnyObject]."

EDIT

编辑

I forgot to force unwrapping the dict["weather"]!.

我忘了强制打开 dict["weather"]!。

if let dict = json as? [String: AnyObject] {
    println(dict)
       if let weatherDictionary = dict["weather"]! as? [AnyObject] {
            println("\nWeather dictionary:\n\n\(weatherDictionary)")
            if let descriptionString = weatherDictionary[0]["description"]! as? String {
                println("\nDescription of the weather is: \(descriptionString)")
        }
    }
}

Note that we should double check for the existence of the first Optional.

请注意,我们应该仔细检查第一个 Optional 是否存在。

if let dict = json as? [String: AnyObject] {
    for key in ["weather", "traffic"] {
        if let dictValue = dict[key] {
            if let subArray = dictValue as? [AnyObject] {
                println(subArray[0])
            }
        } else {
            println("Key '\(key)' not found")
        }
    }
}

回答by Daniel T.

This works fine for me in the playground and in the terminal using env xcrun swift

这对我来说在操场上和在终端中使用很好 env xcrun swift

UPDATED FOR SWIFT 4 AND CODABLE

为 Swift 4 和 Codable 更新

Here is a Swift 4 example using the Codable protocol.

这是一个使用 Codable 协议的 Swift 4 示例。

var jsonStr = "{\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],}"

struct Weather: Codable {
    let id: Int
    let main: String
    let description: String
    let icon: String
}

struct Result: Codable {
    let weather: [Weather]
}

do {
    let weather = try JSONDecoder().decode(Result.self, from: jsonStr.data(using: .utf8)!)
    print(weather)
}
catch {
    print(error)
}

UPDATED FOR SWIFT 3.0

为 Swift 3.0 更新

I have updated the code for Swift 3 and also showed how to wrap the parsed JSON into objects. Thanks for all the up votes!

我更新了 Swift 3 的代码,并展示了如何将解析的 JSON 包装到对象中。感谢大家的投票!

import Foundation

struct Weather {
    let id: Int
    let main: String
    let description: String
    let icon: String
}

extension Weather {
    init?(json: [String: Any]) {
        guard
            let id = json["id"] as? Int,
            let main = json["main"] as? String,
            let description = json["description"] as? String,
            let icon = json["icon"] as? String
        else { return nil }
        self.id = id
        self.main = main
        self.description = description
        self.icon = icon
    }
}

var jsonStr = "{\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],}"

enum JSONParseError: Error {
    case notADictionary
    case missingWeatherObjects
}

var data = jsonStr.data(using: String.Encoding.ascii, allowLossyConversion: false)
do {
    var json = try JSONSerialization.jsonObject(with: data!, options: [])
    guard let dict = json as? [String: Any] else { throw JSONParseError.notADictionary }
    guard let weatherJSON = dict["weather"] as? [[String: Any]] else { throw JSONParseError.missingWeatherObjects }
    let weather = weatherJSON.flatMap(Weather.init)
    print(weather)
}
catch {
    print(error)
}

-- Previous Answer --

-- 上一个答案 --

import Foundation

var jsonStr = "{\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],}"
var data = jsonStr.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
var localError: NSError?
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError)

if let dict = json as? [String: AnyObject] {
    if let weather = dict["weather"] as? [AnyObject] {
        for dict2 in weather {
            let id = dict2["id"]
            let main = dict2["main"]
            let description = dict2["description"]
            println(id)
            println(main)
            println(description)
        }
    }
}

Since I'm still getting up-votes for this answer, I figured I would revisit it for Swift 2.0:

由于我仍然为这个答案投票,我想我会为 Swift 2.0 重新审视它:

import Foundation

var jsonStr = "{\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],}"
var data = jsonStr.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
do {
    var json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)

    if let dict = json as? [String: AnyObject] {
        if let weather = dict["weather"] as? [AnyObject] {
            for dict2 in weather {
                let id = dict2["id"] as? Int
                let main = dict2["main"] as? String
                let description = dict2["description"] as? String
                print(id)
                print(main)
                print(description)
            }
        }
    }

}
catch {
    print(error)
}

The biggest difference is that the variable jsonis no longer an optional type and the do/try/catch syntax. I also went ahead and typed id, main, and description.

最大的区别是变量json不再是可选类型和 do/try/catch 语法。我也继续输入id, main, 和description

回答by dankogai

Try:

尝试:

With it you can go like this:

有了它,你可以这样:

let obj:[String:AnyObject] = [
    "array": [JSON.null, false, 0, "", [], [:]],
    "object":[
        "null":   JSON.null,
        "bool":   true,
        "int":    42,
        "double": 3.141592653589793,
        "string": "a α\t弾\n",
        "array":  [],
        "object": [:]
    ],
    "url":"http://blog.livedoor.com/dankogai/"
]

let json = JSON(obj)

json.toString()
json["object"]["null"].asNull       // NSNull()
json["object"]["bool"].asBool       // true
json["object"]["int"].asInt         // 42
json["object"]["double"].asDouble   // 3.141592653589793
json["object"]["string"].asString   // "a α\t弾\n"
json["array"][0].asNull             // NSNull()
json["array"][1].asBool             // false
json["array"][2].asInt              // 0
json["array"][3].asString           // ""

回答by isair

Using my library (https://github.com/isair/JSONHelper) you can do this with your jsonvariable of type AnyObject:

使用我的库(https://github.com/isair/JSONHelper),您可以使用AnyObject 类型的json变量执行此操作:

var weathers = [Weather]() // If deserialization fails, JSONHelper just keeps the old value in a non-optional variable. This lets you assign default values like this.

if let jsonDictionary = json as? JSONDictionary { // JSONDictionary is an alias for [String: AnyObject]
  weathers <-- jsonDictionary["weather"]
}

Had your array not been under the key "weather", your code would have been just this:

如果您的数组不在关键“天气”下,您的代码将是这样的:

var weathers = [Weather]()
weathers <-- json

Or if you have a json string in your hands you can just pass it as well, instead of creating a JSON dictionary from the string first. The only setup you need to do is writing a Weather class or struct:

或者,如果您手中有一个 json 字符串,您也可以直接传递它,而不是先从字符串创建一个 JSON 字典。您需要做的唯一设置是编写 Weather 类或结构:

struct Weather: Deserializable {
  var id: String?
  var name: String?
  var description: String?
  var icon: String?

  init(data: [String: AnyObject]) {
    id <-- data["id"]
    name <-- data["name"]
    description <-- data["description"]
    icon <-- data["icon"]
  }
}