2014-07-10 14 views
38

Ich versuche, ein JSON zu analysieren, aber ich habe einige Schwierigkeiten mit den Datentypen und vor allem die AnyObject-Typ + Downcasting.Parse JSON in Swift, AnyObject Typ

Betrachten wir das folgende JSON (es ist ein Auszug eines vollständigen 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) 

Mein json ist vom Typ ANYOBJECT:

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

Für mich kann die json wie folgt beschrieben werden! (Ich verwende JSONObjectWithData, um den JSON von einer URL zu erhalten).

Ich möchte dann auf das Wetter-Wörterbuch zugreifen. Hier ist der Code, den ich geschrieben habe.

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 
    } 
} 

Hier ist der Fehler, den ich

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

Ich verstehe nicht, warum dict [ "wetter"] auf einen Subtyp (String, ANYOBJECT) und nicht ANYOBJECT verglichen wird.

Ich habe mein Wörterbuch als [String: AnyObject] deklariert, damit ich mit dem String-Schlüssel auf einen Wert zugreifen kann, sollte ich ein AnyObject haben, nein?

Wenn ich NSDictionary anstelle von [String: AnyObject] verwenden, funktioniert es.

Wenn ich NSArray anstelle von [AnyObject] verwenden, funktioniert es.

- 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

ich die dict zu zwingen, vergessen zu auspackt [ "wetter"] !.

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)") 
     } 
    } 
} 

Beachten Sie, dass wir die Existenz des ersten Optional überprüfen sollten.

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") 
     } 
    } 
} 
+0

Es sieht so aus, als ob der Compiler wirklich versucht, 'dict [" weather "]' als Aufruf von 'subscript (i: DictionaryIndex ) -> (KeyType, ValueType) {get}' zu interpretieren Verwende '[AnyObject]'. Aber ich bin mir nicht sicher warum ... Vielleicht möchten Sie [einen Fehler melden] (http://bugreport.apple.com). – jtbandes

+1

Werfen Sie einen Blick auf: https://github.com/lingoer/SwiftyJSON –

+2

Danke für Ihren Kommentar, aber ich versuche auch, den technischen Grund zu finden und zu verstehen, wie schnell funktioniert. Außerdem scheinen diese beiden Lösungen NSDictionary und NSArray zu verwenden, ich würde gerne Swift Collection-Typen verwenden. – alpennec

Antwort

5

Versuchen:

Mit ihm können Sie so gehen:

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   // "" 
3

meiner Bibliothek verwenden (https://github.com/isair/JSONHelper) Sie können dies tun, mit Ihrem JSON Variable des Typs AnyObjec t:

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"] 
} 

Hat Ihr Array nicht unter dem Schlüssel „Wetter“ gewesen, so wäre der Code gewesen gerade dies:

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

Oder wenn Sie einen JSON-String in der Hand haben, können Sie einfach passieren es auch, anstatt ein JSON-Wörterbuch aus der Zeichenfolge zuerst zu erstellen.Die einzige Einrichtung, die Sie tun müssen, ist das Schreiben einer Wetter Klasse oder Struktur:

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"] 
    } 
} 
33

Dies funktioniert für mich auf dem Spielplatz in Ordnung und im Terminal mit env xcrun swift

für SWIFT AKTUALISIERT 4 UND codierbarem

Hier ist ein Swift 4-Beispiel, das das Codable-Protokoll verwendet.

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) 
} 

für SWIFT 3.0

AKTUALISIERT

ich den Code für Swift 3 aktualisiert haben und auch gezeigt, wie die geparsten JSON in Objekte zu wickeln. Danke für die vielen Stimmen!

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) 
} 

- Zurück Antwort -

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) 
     } 
    } 
} 

Da ich oben-Stimmen für diese Antwort noch bin immer, dachte ich, ich es für Swift 2.0 denken würde:

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) 
} 

Der größte Unterschied besteht darin, dass die Variable json nicht länger ein optionaler Typ und die Syntax do/try/catch ist. Ich ging auch voran und tippte id, main und description.

+0

Das funktioniert nicht, wenn "Wetter" ein Swift-Array oder ein Wörterbuch ist. –

Verwandte Themen