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 시리얼화의 결과를 실제 타입으로 캐스트 할 필요가 있습니다.
에서는 「」를 사용하고 있습니다.URLSession
Swift 네이티브 타입 전용
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] { ...
키)로 값을 .OneOfSupportedJSONTypes
JSON의 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 로 디코딩할 수 .Date
dateDecodingStrategy
.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.jsonObject
에Forecast
JSON 문자열을 사용하는 편리한 컨스트럭터도 포함되어 있기 때문에 강한 타입의 파일을 신속하게 해석할 수 있습니다.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 세션 및 서드파티 라이브러리를 통해 이를 달성할 수 있습니다.Alamofire
URL 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" 보일러 플레이트를 제거하고 기능적 접근법에 따라 강제로 래프를 풉니다.
- 여기 JSON이 있습니다.옵션인 JSON 또는 통상적인 것을 사용할 수 있습니다.이 예에서는 옵션을 사용하고 있습니다.
let json: Dictionary<String, Any>? = ["current": ["temperature": 10]]
- 도우미 기능한 번만 작성한 후 사전과 함께 재사용해야 합니다.
/// 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
}
- 그리고 여기에 우리의 마법이 있습니다. 가치를 끌어냅니다.
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
'programing' 카테고리의 다른 글
SAP UI5와 다른 Javascript 프레임워크 비교 (0) | 2023.03.22 |
---|---|
React 경로용 Apache 서버 설정 방법 (0) | 2023.03.22 |
Sugary 키, Synthetic 키, 인공 키에는 차이가 있습니까? (0) | 2023.03.22 |
AngularJS - ng옵션:그룹명, 라벨 순으로 주문하는 방법 (0) | 2023.03.22 |
Python 3 JSON API 가져오기 및 구문 분석 (0) | 2023.03.22 |