web-dev-qa-db-ja.com

Swift 4 JSONSerialization.jsonObject

Xcode 9.2とSwift 4.を使用しています。返されたjsonデータがnullかどうかを確認するにはどうすればよいですか?エラーType 'Any' has no subscript membersif json[0]["data"]

var json: NSMutableArray = []
var newsArray: NSMutableArray = []

let url = URLFactory()
var data = try! NSData(contentsOf: url.getURL()) as Data
do {
    json = try JSONSerialization.jsonObject(with: data, options: []) as! NSMutableArray
    if json[0]["data"] {
        // data is not null
    }
} catch let error as NSError {
    // handle error
}

私のJSONは次のようなものを返します:

{
   "data":
   [
      {
         "news_id":123,
         "title":"title",
         "news_date":"2017-02-08 21:46:06",
         "news_url":"url",
         "short_description":"description",
         "category_id":4,
         "category_name":"Health",
         "latlng":
         [
            {
               "lat":"43.003429",
               "lng":"-78.696335"
            }
         ]
      }
      { ....
}
8
Jumana Alhaddad

Swift 4を使用している場合、JSONDecoderをお勧めします:

最初に、解析されたデータを保持するタイプを定義します。

_struct ResponseObject: Codable {
    let data: [NewsItem]
}

struct NewsItem: Codable {
    let newsId: Int
    let title: String
    let newsDate: Date
    let newsURL: URL
    let shortDescription: String
    let categoryID: Int
    let categoryName: String
    let coordinates: [Coordinate]

    // because your json keys don't follow normal Swift naming convention, use CodingKeys to map these property names to JSON keys

    enum CodingKeys: String, CodingKey {
        case newsId = "news_id"
        case title
        case newsDate = "news_date"
        case newsURL = "news_url"
        case shortDescription = "short_description"
        case categoryID = "category_id"
        case categoryName = "category_name"
        case coordinates = "latlng"
    }
}

struct Coordinate: Codable {
    let lat: String
    let lng: String
}
_

そして、あなたはそれを解析することができます:

_let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
    let responseObject = try decoder.decode(ResponseObject.self, from: data)
    print(responseObject.data)
} catch {
    print(error)
}
_

明らかに、JSONが異なる場合は、それに応じてオブジェクトを変更する必要があるかもしれません(たとえば、latlngが座標の配列であることに奇妙に思いました)。また、これらの文字列の一部を数値に変換する場合は、カスタムinit(from:)メソッドを定義できますが、代わりにJSONを修正したいのです(なぜlatlngが文字列値を返すのではなく、数値)。

詳細については、「 カスタムタイプのエンコードとデコード 」を参照してください。


余談ですが、私はこのパターンに反対することをお勧めします(これはネットワークリクエストロジックですが、NSDataは削除されています)。

_let data = try! Data(contentsOf: url.getURL())
_

これはデータを同期的に取得します。

  • データの取得中にアプリがフリーズし、UXが低下します。
  • 凍結されたアプリを探すウォッチドッグプロセスによってアプリが強制終了されるリスクがあります。そして
  • 堅牢なエラー処理がないため、ネットワーク要求が失敗するとクラッシュします。

URLSessionの使用をお勧めします:

_let task = URLSession.shared.dataTask(with: url.getURL()) { data, _, error in
    guard let data = data, error == nil else {
        print(error ?? "Unknown error")
        return
    }

    // now parse `data` like shown above

    // if you then need to update UI or model objects, dispatch that back
    // to the main queue:

    DispatchQueue.main.async {
        // use `responseObject.data` to update model objects and/or UI here
    }
}
task.resume()
_
18
Rob