programing

Swift 3에서의 JSON의 올바른 해석

kingscode 2023. 3. 22. 22:49
반응형

Swift 3에서의 JSON의 올바른 해석

JSON 응답을 가져와 결과를 변수에 저장하려고 합니다.Xcode 8 GM 버전이 출시되기 전까지 이전 Swift 버전에서 이 코드가 작동되었습니다.Stack Overflow: Swift 2 Parsing JSON - Swift 3에서 유형 'Any Object'JSON Parsing값을 서브스크립트할 수 없습니다.

그러나, 거기에 전달된 아이디어는 이 시나리오에 적용되지 않는 것 같습니다.

Swift 3에서 JSON 응답을 올바르게 해석하려면 어떻게 해야 합니까?Swift 3에서 JSON을 읽는 방법에 변화가 있습니까?

아래는 문제의 코드입니다(놀이터에서 실행할 수 있습니다).

import Cocoa

let url = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"

if let url = NSURL(string: url) {
    if let data = try? Data(contentsOf: url as URL) {
        do {
            let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments)

        //Store response in NSDictionary for easy access
        let dict = parsedData as? NSDictionary

        let currentConditions = "\(dict!["currently"]!)"

        //This produces an error, Type 'Any' has no subscript members
        let currentTemperatureF = ("\(dict!["currently"]!["temperature"]!!)" as NSString).doubleValue

            //Display all current conditions from API
            print(currentConditions)

            //Output the current temperature in Fahrenheit
            print(currentTemperatureF)

        }
        //else throw an error detailing what went wrong
        catch let error as NSError {
            print("Details of JSON parsing error:\n \(error)")
        }
    }
}

편집: 다음 예시는 다음 이후의 API 호출 결과입니다.print(currentConditions)

["icon": partly-cloudy-night, "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precipIntensity": 0, "windSpeed": 6.04, "summary": Partly Cloudy, "ozone": 321.13, "temperature": 49.45, "dewPoint": 41.75, "apparentTemperature": 47, "windBearing": 332, "cloudCover": 0.28, "time": 1480846460]

우선 리모트 URL에서 데이터를 동기적으로 로드하지 않고 항상 다음과 같은 비동기 방식을 사용합니다.URLSession.

'임의'에는 첨자 멤버가 없습니다.

오브젝트의 하지 못하기 때문에 를 들어, 「중간 오브젝트」의 종류currently["currently"]!["temperature"]하고 있기 때문에 수집 타입을 사용하고 있습니다.NSDictionary이치노

또한 Swift 3에서는 모든 서브스크립트 객체의 유형을 컴파일러에 알려야 합니다.

JSON 시리얼화의 결과를 실제 타입으로 캐스트 할 필요가 있습니다.

에서는 「」를 사용하고 있습니다.URLSessionSwift 네이티브 타입 전용

let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"

let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
  if error != nil {
    print(error)
  } else {
    do {

      let parsedData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
      let currentConditions = parsedData["currently"] as! [String:Any]

      print(currentConditions)

      let currentTemperatureF = currentConditions["temperature"] as! Double
      print(currentTemperatureF)
    } catch let error as NSError {
      print(error)
    }
  }

}.resume()

"/"의 키currentConditions 수 있다

 let currentConditions = parsedData["currently"] as! [String:Any]

  for (key, value) in currentConditions {
    print("\(key) - \(value) ")
  }

★★★★★★★★에 관한 주의사항jsonObject(with data:

시사하는 바가 있다..mutableContainers ★★★★★★★★★★★★★★★★★」.mutableLeaves재빠르다두은 결과를 ""에 Objective-C 입니다.NSMutable...★★★★★★★★★★★★★★★★★★★★★★★★★★★★★var로 변경 옵션 중하고 결과를 iable에 할당합니다.let을 사용법게다가 대부분의 실장에서는, 어쨌든 역직렬화된 JSON은 뮤트 되지 않습니다.

은 Swift입니다..allowFragments값JSON')일합니다.String,Number,Bool ★★★★★★★★★★★★★★★★★」null 유형중 1')가 ( )array ★★★★★★★★★★★★★★★★★」dictionary단, 합니다.」 「 」 「 」options[ No options ](옵션 없음)을 의미합니다.

===========================================================================

JSON 해석에 관한 일반적인 고려사항

JSON은 잘 배열된 텍스트 형식입니다.JSON 문자열을 읽는 것은 매우 쉽습니다.을 잘 읽어보세요.수집 유형 2개와 값 유형 4개 등 6개만 있습니다.


수집 유형은 다음과 같습니다.

  • 어레이 - JSON: 각 괄호 안의 객체[] - 빨리[Any], 의 경우 '''[[String:Any]]
  • 사전 - JSON: 물결 괄호 안의 개체{} - 빨리[String:Any]

값 유형은 다음과 같습니다.

  • 문자열 - JSON: 큰따옴표로 둘러싸인 임의의 값"Foo" , 이븐 , 이븐."123" ★★★★★★★★★★★★★★★★★」"false": – 신성:String
  • 숫자 - JSON: 숫자 값이 큰따옴표로 묶이지 않습니다.123 ★★★★★★★★★★★★★★★★★」123.0: – 신성:Int ★★★★★★★★★★★★★★★★★」Double
  • BOOL - JSON:true ★★★★★★★★★★★★★★★★★」false 큰따옴표로 묶지 않음– Swift :true ★★★★★★★★★★★★★★★★★」false
  • 특수 - JSON:null: – 신성:NSNull

의 모든 는 JSON으로 .String


기본적으로 옵션 바인딩을 사용하여 옵션을 안전하게 언랩하는 것이 좋습니다.

(「」)인 .{}를 ( )으로 합니다.[String:Any]

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...

키)로 값을 .OneOfSupportedJSONTypesJSON의 JSON이다.

if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
    print(foo)
} 

「」 「」 )인 .[]를 ( )으로 합니다.[[String:Any]]

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...

그리고 그 배열 전체를 반복한다.

for item in parsedData {
    print(item)
}

특정 인덱스 검사에서 항목이 필요한 경우 인덱스가 존재하는지 여부도 확인합니다.

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
   let item = parsedData[2] as? OneOfSupportedJSONTypes {
      print(item)
    }
}

타입이 값 중 JSON을 ..allowFragments 값 예: "예: "예: "는" 값 유형으로 합니다.

if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...

Apple은 Swift 블로그에 다음과 같은 포괄적인 기사를 게재했습니다.Swift의 JSON과의 연계


===========================================================================

4에서는 Swift 4+입니다.Codable로 직접 해석할 수 .protocol은 JSON을 / 해석할 수 있습니다.

예를 들어 질문에서 지정된 JSON 샘플(약간 수정됨)

let jsonString = """
{"icon": "partly-cloudy-night", "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precip_intensity": 0, "wind_speed": 6.04, "summary": "Partly Cloudy", "ozone": 321.13, "temperature": 49.45, "dew_point": 41.75, "apparent_temperature": 47, "wind_bearing": 332, "cloud_cover": 0.28, "time": 1480846460}
"""

할 수 Weather 타입은 재빠르다 밖에도몇 옵션이 .

  • 를 나타내는 URL 할 수 URL
  • time는 수음음음음음음음음음 integer integer integer integer integer integer integer integer integer integer integer integer 로 디코딩할 수 .DatedateDecodingStrategy .secondsSince1970.
  • snaked_cased JSON 키는 를 사용하여 camelCase로 변환할 수 있습니다.keyDecodingStrategy .convertFromSnakeCase

struct Weather: Decodable {
    let icon, summary: String
    let pressure: Double, humidity, windSpeed : Double
    let ozone, temperature, dewPoint, cloudCover: Double
    let precipProbability, precipIntensity, apparentTemperature, windBearing : Int
    let time: Date
}

let data = Data(jsonString.utf8)
do {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .secondsSince1970
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let result = try decoder.decode(Weather.self, from: data)
    print(result)
} catch {
    print(error)
}

기타 코드 가능한 소스:

Xcode 8 Beta 6 for Swift 3에서 발생한 큰 변화는 ID가 현재 Import되는 것입니다.Any보다는AnyObject.

즉,parsedData가장 가능성이 높은 사전으로 반환됩니다.[Any:Any]디버거를 사용하지 않고서는 정확히 어떤 캐스트를 캐스팅할지 알 수 없습니다.NSDictionary동작합니다만, 에러는 다음과 같습니다.dict!["currently"]!타입이 있다Any

그럼 어떻게 해결할까요?당신이 언급한 걸 보면dict!["currently"]!는 딕셔너리이므로 다양한 옵션이 있습니다.

처음에는 다음과 같은 작업을 수행할 수 있습니다.

let currentConditionsDictionary: [String: AnyObject] = dict!["currently"]! as! [String: AnyObject]  

그러면 값을 쿼리할 수 있는 사전 개체가 제공되므로 다음과 같은 온도를 얻을 수 있습니다.

let currentTemperatureF = currentConditionsDictionary["temperature"] as! Double

또는 필요에 따라서, 다음의 순서를 실행할 수 있습니다.

let currentTemperatureF = (dict!["currently"]! as! [String: AnyObject])["temperature"]! as! Double

이것이 도움이 되었으면 합니다만, 테스트하기 위한 샘플 앱을 작성할 시간이 없었습니다.

마지막 주의사항: 가장 쉬운 방법은 JSON payload를 단순히 캐스트하는 것입니다.[String: AnyObject]시작부터요.

let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as! Dictionary<String, AnyObject>
let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"

let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!

do {
    let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
    if let names = json["names"] as? [String] 
{
        print(names)
}
} catch let error as NSError {
    print("Failed to load: \(error.localizedDescription)")
}

저는 바로 이 목적을 위해 퀵타입을 만들었습니다.샘플 JSON을 붙여넣기만 하면 quicktype은 API 데이터에 대해 다음 유형의 계층을 생성합니다.

struct Forecast {
    let hourly: Hourly
    let daily: Daily
    let currently: Currently
    let flags: Flags
    let longitude: Double
    let latitude: Double
    let offset: Int
    let timezone: String
}

struct Hourly {
    let icon: String
    let data: [Currently]
    let summary: String
}

struct Daily {
    let icon: String
    let data: [Datum]
    let summary: String
}

struct Datum {
    let precipIntensityMax: Double
    let apparentTemperatureMinTime: Int
    let apparentTemperatureLowTime: Int
    let apparentTemperatureHighTime: Int
    let apparentTemperatureHigh: Double
    let apparentTemperatureLow: Double
    let apparentTemperatureMaxTime: Int
    let apparentTemperatureMax: Double
    let apparentTemperatureMin: Double
    let icon: String
    let dewPoint: Double
    let cloudCover: Double
    let humidity: Double
    let ozone: Double
    let moonPhase: Double
    let precipIntensity: Double
    let temperatureHigh: Double
    let pressure: Double
    let precipProbability: Double
    let precipIntensityMaxTime: Int
    let precipType: String?
    let sunriseTime: Int
    let summary: String
    let sunsetTime: Int
    let temperatureMax: Double
    let time: Int
    let temperatureLow: Double
    let temperatureHighTime: Int
    let temperatureLowTime: Int
    let temperatureMin: Double
    let temperatureMaxTime: Int
    let temperatureMinTime: Int
    let uvIndexTime: Int
    let windGust: Double
    let uvIndex: Int
    let windBearing: Int
    let windGustTime: Int
    let windSpeed: Double
}

struct Currently {
    let precipProbability: Double
    let humidity: Double
    let cloudCover: Double
    let apparentTemperature: Double
    let dewPoint: Double
    let ozone: Double
    let icon: String
    let precipIntensity: Double
    let temperature: Double
    let pressure: Double
    let precipType: String?
    let summary: String
    let uvIndex: Int
    let windGust: Double
    let time: Int
    let windBearing: Int
    let windSpeed: Double
}

struct Flags {
    let sources: [String]
    let isdStations: [String]
    let units: String
}

또한 종속성이 없는 마샬링 코드를 생성하여 반환값을 유도합니다.JSONSerialization.jsonObjectForecastJSON 문자열을 사용하는 편리한 컨스트럭터도 포함되어 있기 때문에 강한 타입의 파일을 신속하게 해석할 수 있습니다.Forecast값을 지정하고 필드에 액세스합니다.

let forecast = Forecast.from(json: jsonString)!
print(forecast.daily.data[0].windGustTime)

npm부터 퀵 타입을 설치할 수 있습니다.npm i -g quicktype또는 웹 UI를 사용하여 생성된 코드 전체를 놀이터에 붙여넣습니다.

의 갱신isConnectToNetwork-Function나중에 이 게시물 덕분에

나는 그것을 위한 추가 방법을 썼다.

import SystemConfiguration

func loadingJSON(_ link:String, postString:String, completionHandler: @escaping (_ JSONObject: AnyObject) -> ()) {

    if(isConnectedToNetwork() == false){
        completionHandler("-1" as AnyObject)
        return
    }

    let request = NSMutableURLRequest(url: URL(string: link)!)
    request.httpMethod = "POST"
    request.httpBody = postString.data(using: String.Encoding.utf8)

    let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
        guard error == nil && data != nil else { // check for fundamental networking error
            print("error=\(error)")
            return
        }

        if let httpStatus = response as? HTTPURLResponse , httpStatus.statusCode != 200 { // check for http errors
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
            print("response = \(response)")
        }
        //JSON successfull
        do {
            let parseJSON = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
            DispatchQueue.main.async(execute: {
                completionHandler(parseJSON as AnyObject)
            });
        } catch let error as NSError {
            print("Failed to load: \(error.localizedDescription)")
        }
    }
    task.resume()
}

func isConnectedToNetwork() -> Bool {

    var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
    zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
    zeroAddress.sin_family = sa_family_t(AF_INET)

    let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
            SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
        }
    }

    var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
    if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
        return false
    }

    let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
    let ret = (isReachable && !needsConnection)

    return ret
}

앱에서 원하는 위치에 쉽게 호출할 수 있습니다.

loadingJSON("yourDomain.com/login.php", postString:"email=\(userEmail!)&password=\(password!)") { parseJSON in

    if(String(describing: parseJSON) == "-1"){
        print("No Internet")
    } else {

    if let loginSuccessfull = parseJSON["loginSuccessfull"] as? Bool {
        //... do stuff
    }
}

이것은 당신의 문제를 해결하는 또 다른 방법입니다.아래의 솔루션을 확인해 주십시오.도움이 되길 바랍니다.

let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"
let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
    let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
    if let names = json["names"] as? [String] {
        print(names)
    }
} catch let error as NSError {
    print("Failed to load: \(error.localizedDescription)")
}

API 인터랙션 방식에 문제가 있습니다.JSON 해석은 구문에서만 변경됩니다.주요 문제는 데이터를 가져오는 방식에 있습니다.사용하고 있는 것은 데이터를 동기화하는 방법입니다.모든 경우에 효과가 있는 것은 아닙니다.데이터를 가져오기 위한 비동기 방식을 사용해야 합니다.이렇게 하면 API를 통해 데이터를 요청하고 데이터 응답을 기다려야 합니다.다음과 같은 URL 세션 및 서드파티 라이브러리를 통해 이를 달성할 수 있습니다.AlamofireURL Session 메서드의 코드는 다음과 같습니다.

let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"
let url = URL.init(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
    guard error == nil else {
        print(error)
    }
    do {
        let Data = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
        // Note if your data is coming in Array you should be using [Any]()
        //Now your data is parsed in Data variable and you can use it normally
        let currentConditions = Data["currently"] as! [String:Any]
        print(currentConditions)
        let currentTemperatureF = currentConditions["temperature"] as! Double
        print(currentTemperatureF)
    } catch let error as NSError {
        print(error)
    }
}.resume()
{
    "User":[
      {
        "FirstUser":{
        "name":"John"
        },
       "Information":"XY",
        "SecondUser":{
        "name":"Tom"
      }
     }
   ]
}

이전 json을 사용하여 모델을 만드는 경우 이 링크 [blog]:http://www.jsoncafe.com에서 코드 가능한 구조 또는 모든 형식을 생성합니다.

모델

import Foundation
struct RootClass : Codable {
    let user : [Users]?
    enum CodingKeys: String, CodingKey {
        case user = "User"
    }

    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        user = try? values?.decodeIfPresent([Users].self, forKey: .user)
    }
}

struct Users : Codable {
    let firstUser : FirstUser?
    let information : String?
    let secondUser : SecondUser?
    enum CodingKeys: String, CodingKey {
        case firstUser = "FirstUser"
        case information = "Information"
        case secondUser = "SecondUser"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        firstUser = try? FirstUser(from: decoder)
        information = try? values?.decodeIfPresent(String.self, forKey: .information)
        secondUser = try? SecondUser(from: decoder)
    }
}
struct SecondUser : Codable {
    let name : String?
    enum CodingKeys: String, CodingKey {
        case name = "name"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        name = try? values?.decodeIfPresent(String.self, forKey: .name)
    }
}
struct FirstUser : Codable {
    let name : String?
    enum CodingKeys: String, CodingKey {
        case name = "name"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        name = try? values?.decodeIfPresent(String.self, forKey: .name)
    }
}

해석

    do {
        let res = try JSONDecoder().decode(RootClass.self, from: data)
        print(res?.user?.first?.firstUser?.name ?? "Yours optional value")
    } catch {
        print(error)
    }

스위프트는 강력한 유형 추론을 가지고 있다."if let" 또는 "guard let" 보일러 플레이트를 제거하고 기능적 접근법에 따라 강제로 래프를 풉니다.

  1. 여기 JSON이 있습니다.옵션인 JSON 또는 통상적인 것을 사용할 수 있습니다.이 예에서는 옵션을 사용하고 있습니다.
let json: Dictionary<String, Any>? = ["current": ["temperature": 10]]
  1. 도우미 기능한 번만 작성한 후 사전과 함께 재사용해야 합니다.
/// Curry
public func curry<A, B, C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
    return { a in
        { f(a, $0) }
    }
}

/// Function that takes key and optional dictionary and returns optional value
public func extract<Key, Value>(_ key: Key, _ json: Dictionary<Key, Any>?) -> Value? {
    return json.flatMap {
        cast($0[key])
    }
}

/// Function that takes key and return function that takes optional dictionary and returns optional value
public func extract<Key, Value>(_ key: Key) -> (Dictionary<Key, Any>?) -> Value? {
    return curry(extract)(key)
}

/// Precedence group for our operator
precedencegroup RightApplyPrecedence {
    associativity: right
    higherThan: AssignmentPrecedence
    lowerThan: TernaryPrecedence
}

/// Apply. g § f § a === g(f(a))
infix operator § : RightApplyPrecedence
public func §<A, B>(_ f: (A) -> B, _ a: A) -> B {
    return f(a)
}

/// Wrapper around operator "as".
public func cast<A, B>(_ a: A) -> B? {
    return a as? B
}
  1. 그리고 여기에 우리의 마법이 있습니다. 가치를 끌어냅니다.
let temperature = (extract("temperature") § extract("current") § json) ?? NSNotFound

코드 한 줄만 쓸 수 있고 강제 풀림이나 수동식 캐스팅은 없습니다.이 코드는 놀이터에서 작동하므로 복사하여 확인할 수 있습니다.다음은 GitHub에서의 구현입니다.

Swift 5 API에서 데이터를 가져올 수 없습니다.json을 구문 분석하는 가장 쉬운 방법은 사용입니다.Decodable프로토콜입니다. ★★★Codable )Encodable & Decodable의 경우:

let json = """
{
    "dueDate": {
        "year": 2021,
        "month": 2,
        "day": 17
    }
}
"""

struct WrapperModel: Codable {
    var dueDate: DueDate
}

struct DueDate: Codable {
    var year: Int
    var month: Int
    var day: Int
}

let jsonData = Data(json.utf8)

let decoder = JSONDecoder()

do {
    let model = try decoder.decode(WrapperModel.self, from: jsonData)
    print(model)
} catch {
    print(error.localizedDescription)
}

언급URL : https://stackoverflow.com/questions/39423367/correctly-parsing-json-in-swift-3

반응형