แยกวิเคราะห์ JSON อย่างถูกต้องใน Swift 3


123

ฉันพยายามดึงการตอบสนอง JSON และเก็บผลลัพธ์ไว้ในตัวแปร ฉันเคยใช้รหัสนี้ในเวอร์ชันก่อนหน้าของ Swift จนกระทั่ง Xcode 8 เวอร์ชัน GM เปิดตัว ฉันได้ดูโพสต์ที่คล้ายกันสองสามรายการใน StackOverflow: Swift 2 Parsing JSON - ไม่สามารถห้อยค่าประเภท 'AnyObject'และJSON Parsing ใน Swift 3ได้

อย่างไรก็ตามดูเหมือนว่าความคิดที่นำเสนอนั้นจะไม่สามารถใช้ได้กับสถานการณ์นี้

ฉันจะแยกวิเคราะห์การตอบกลับ JSON ใน Swift 3 อย่างถูกต้องได้อย่างไร มีอะไรเปลี่ยนแปลงไปบ้างในการอ่าน JSON ใน Swift 3?

ด้านล่างนี้คือรหัสที่เป็นปัญหา (สามารถเรียกใช้ในสนามเด็กเล่น):

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]

คุณใส่ข้อมูลตัวอย่างที่ส่งคืนจากการเรียก API ได้ไหม
ผู้ใช้

1
ใช่ฉันเพิ่งเพิ่มตัวอย่างของผลลัพธ์ที่พิมพ์หลังจากพิมพ์ (currentConditions) หวังว่าจะช่วยได้
user2563039

แยกวิเคราะห์ json ใน swift4 โดยใช้โปรโตคอล Codable stackoverflow.com/a/52931265/9316566
Naser Mohamed

คำตอบ:


172

ครั้งแรกของทั้งหมดไม่เคยโหลดข้อมูลพร้อมกันจากระยะไกล URL ที่URLSessionใช้วิธีการไม่ตรงกันเสมอเช่น

"ใด ๆ " ไม่มีสมาชิกตัวห้อย

เกิดขึ้นเนื่องจากคอมไพลเลอร์ไม่ทราบว่าวัตถุกลางเป็นประเภทใด (ตัวอย่างเช่นcurrentlyใน["currently"]!["temperature"]) และเนื่องจากคุณใช้ชนิดคอลเล็กชัน Foundation เช่น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ตัวเลือกที่ไร้สาระอย่างสมบูรณ์ใน Swift สองตัวเลือกคืออ็อพชัน Objective-C ดั้งเดิมเพื่อกำหนดผลลัพธ์ให้กับNSMutable...อ็อบเจ็กต์ ใน Swift variable ใด ๆจะไม่แน่นอนโดยค่าเริ่มต้นและการส่งผ่านตัวเลือกใด ๆ เหล่านั้นและการกำหนดผลลัพธ์เป็นletค่าคงที่จะไม่มีผลเลย ยิ่งไปกว่านั้นการใช้งานส่วนใหญ่จะไม่กลายพันธุ์ JSON ที่ไม่ได้กำหนดค่ามาตรฐานอีกต่อไป

เท่านั้น (หายาก) ตัวเลือกซึ่งจะเป็นประโยชน์ในสวิฟท์.allowFragmentsซึ่งเป็นสิ่งจำเป็นถ้าหากวัตถุราก JSON อาจจะเป็นประเภทค่า ( String, Number, Boolหรือnull) มากกว่าหนึ่งในชนิดของคอลเลกชัน ( arrayหรือdictionary) แต่ปกติละเว้นoptionsพารามิเตอร์ซึ่งหมายความว่าไม่มีตัวเลือก

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

ข้อควรพิจารณาทั่วไปบางประการในการแยกวิเคราะห์ JSON

JSON เป็นรูปแบบข้อความที่มีการจัดเรียงอย่างดี การอ่านสตริง JSON นั้นง่ายมาก อ่านสตริงอย่างระมัดระวัง มีเพียงหกประเภทเท่านั้น - ประเภทคอลเลกชันสองประเภทและประเภทมูลค่าสี่ประเภท


ประเภทคอลเลกชันคือ

  • Array - JSON: ออบเจ็กต์ในวงเล็บเหลี่ยม[]- Swift: [Any]แต่ในกรณีส่วนใหญ่[[String:Any]]
  • พจนานุกรม - JSON: วัตถุในวงเล็บปีกกา{}- Swift:[String:Any]

ประเภทค่าคือ

  • สตริง - JSON: ค่าใด ๆ ในราคาคู่"Foo"แม้"123"หรือ"false"- สวิฟท์:String
  • Number - JSON: ค่าตัวเลขที่ไม่อยู่ในเครื่องหมายคำพูดคู่123หรือ123.0- Swift: IntหรือDouble
  • Bool - JSON: trueหรือfalse ไม่อยู่ในเครื่องหมายคำพูดคู่ - Swift: trueหรือfalse
  • null - JSON: null- Swift:NSNull

ตามข้อกำหนด JSON Stringกุญแจทั้งหมดในพจนานุกรมจะต้อง


โดยทั่วไปจะแนะนำให้ใช้การผูกเสริมเพื่อแกะตัวเลือกอย่างปลอดภัย

หากอ็อบเจ็กต์รูทเป็นดิกชันนารี ( {}) ส่งประเภทเป็น[String:Any]

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

และดึงค่าด้วยคีย์ด้วย ( OneOfSupportedJSONTypesเป็นคอลเล็กชัน 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 Blog: Working with JSON in Swift


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

ใน Swift 4+ Codableโปรโตคอลให้วิธีที่สะดวกกว่าในการแยกวิเคราะห์ 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}
"""

สามารถถอดรหัสเข้าไปที่ Weatherstruct ประเภทของ Swift นั้นเหมือนกับที่อธิบายไว้ข้างต้น มีตัวเลือกเพิ่มเติมบางประการ:

  • สตริงที่แสดงถึงURLสามารถถอดรหัสได้โดยตรงเป็นURL.
  • timeจำนวนเต็มสามารถถอดรหัสเป็นด้วย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)
}

แหล่งที่มาที่สามารถเข้ารหัสอื่น ๆ :


สิ่งนี้มีประโยชน์มาก ฉันแค่สงสัยว่าทำไมโค้ดด้านบนไม่แสดงผลลัพธ์ที่มองเห็นได้เมื่อทำงานในสนามเด็กเล่น
user2563039

ดังที่ได้กล่าวไว้ข้างต้นคุณต้องส่งผลลัพธ์ที่ไม่ชัดเจนของdict!["currently"]!ไปยังพจนานุกรมที่คอมไพเลอร์สามารถสรุปการสมัครสมาชิกคีย์ในภายหลังได้อย่างปลอดภัย
vadian

1
บทความของ Apple นั้นค่อนข้างเจ๋ง แต่ฉันไม่สามารถใช้งาน swift 3 ได้ด้วยเหตุผลบางประการ มันบ่นเกี่ยวกับ Type [String: Any]? ไม่มีสมาชิกตัวห้อย มันมีปัญหาอื่น ๆ ด้วย แต่ฉันก็สามารถแก้ไขมันได้ ใครมีตัวอย่างโค้ดที่ใช้งานได้จริง
เฉด

@Shades ถามคำถามและโพสต์รหัสของคุณ ปัญหาส่วนใหญ่ของคุณเกี่ยวข้องกับตัวเลือกที่ไม่ได้ห่อหุ้ม
vadian

คุณใส่ข้อมูลตัวอย่างที่ส่งคืนจากการเรียก API ได้ไหม
ผู้ใช้

12

การเปลี่ยนแปลงขนาดใหญ่ที่เกิดขึ้นกับ Xcode 8 Beta 6 สำหรับสวิฟท์ 3 เป็นรหัสว่าขณะนี้การนำเข้าเป็นมากกว่าAnyAnyObject

ซึ่งหมายความว่าจะกลับมาเป็นพจนานุกรมของส่วนใหญ่มีแนวโน้มที่มีชนิด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 ลงใน[String: AnyObject]ตอนเริ่มต้น

let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as! Dictionary<String, AnyObject>

dict!["currently"]! as! [String: String]จะผิดพลาด
Vadian

จุดที่มันพังคือ ["อุณหภูมิ"] !! และพยายามส่ง String ไปยัง NSString - ตรวจสอบว่าโซลูชันใหม่ใช้งานได้swiftlang.ng.bluemix.net/#/repl/57d3bc683a422409bf36c391
discorevilo

[String: String]ไม่สามารถทำงานได้เลยเนื่องจากมีค่าตัวเลขสองสามค่า มันใช้ไม่ได้ในMac Playground
vadian

@vadian อาใช่ฉันไม่ได้สังเกตเห็นสิ่งเหล่านั้นและกล่องทรายที่รวดเร็วซ่อนพวกเขาไว้อย่างเป็นประโยชน์! - แก้ไข [อีกครั้ง] ตอนนี้ (และทดสอบบน macOS) ขอบคุณที่ชี้ให้เห็น :)
discorevilo

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

5

ฉันสร้างQuicktypeขึ้นมาเพื่อจุดประสงค์นี้ เพียงวาง 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
}

นอกจากนี้ยังสร้างโค้ด marshaling ที่ไม่ต้องพึ่งพาเพื่อเกลี้ยกล่อมค่าที่ส่งคืนJSONSerialization.jsonObjectไปยัง a Forecastซึ่งรวมถึงคอนสตรัคเตอร์อำนวยความสะดวกที่รับสตริง JSON เพื่อให้คุณสามารถแยกวิเคราะห์Forecastค่าที่พิมพ์อย่างหนักและเข้าถึงฟิลด์ได้อย่างรวดเร็ว:

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

คุณสามารถติดตั้ง Quicktype จาก npm ด้วยnpm i -g quicktypeหรือใช้ UI ของเว็บเพื่อรับโค้ดที่สร้างขึ้นทั้งหมดเพื่อวางลงในสนามเด็กเล่นของคุณ


ลิงก์ล้มเหลว
..

ประหยัดเวลาได้อย่างยอดเยี่ยม! เก่งมาก!
marko

1
เครื่องมือที่ยอดเยี่ยม ขอบคุณ
Ahmet Ardal

4

อัปเดตใน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
    }
}

Marco วิธีใดที่ดีที่สุดในการเพิ่มฟังก์ชันนี้ในโครงการ ใน contorller หรือรุ่นใด
KamalPanhwar

เฮ้คุณเพิ่งเพิ่ม func loadingJSON (... ) วิธีแรกในไฟล์หรือคลาสที่รวดเร็วเป็นพิเศษ หลังจากนั้นคุณสามารถเรียกสิ่งนี้ได้จากผู้ควบคุมทุกคนในโครงการของคุณ
Marco Weber

ฉันได้ลองใช้แล้ว แต่ฉันชอบความคิดในการสาธิตโซลูชันเต็มรูปแบบและวิธีการใช้งานรวมถึงวิธีการช่วยเหลือ isConnectedToNetwork () ฉันคิดว่าจะใช้มันอย่างไรในรหัสที่ดี
Amr Angry

ดังนั้นฉันจึงใช้ไฟล์ที่รวดเร็วใหม่ (คลิกซ้ายบนแผนผังโครงการของคุณไฟล์ใหม่ ... ไฟล์ที่รวดเร็ว) และเรียกมันว่า jsonhelper.swift ในไฟล์นี้คุณใส่รหัสแรกคือ loadingJSON () และ isConnectedToNetwork () หลังจากนั้นคุณสามารถใช้สองฟังก์ชันนี้ในทุกส่วนของโปรเจ็กต์ของคุณ ตัวอย่างเช่นใน loginVC เป็นการกระทำของปุ่มเข้าสู่ระบบคุณสามารถใช้รหัสที่สองได้แน่นอนว่าคุณต้องเปลี่ยนโดเมนสตริงโพสต์และค่า paseJson (parseJSON ["loginSuccessfull"]) เพื่อให้ตรงกับ ไฟล์ php ของคุณ
Marco Weber

0

Swift มีการอนุมานประเภทที่มีประสิทธิภาพ ให้กำจัดหม้อไอน้ำแบบ "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


0

นี่เป็นอีกวิธีหนึ่งในการแก้ปัญหาของคุณ ดังนั้นโปรดตรวจสอบวิธีแก้ปัญหาด้านล่าง หวังว่ามันจะช่วยคุณได้

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

0

ปัญหาเกิดจากวิธีการโต้ตอบกับ API การแยกวิเคราะห์ JSON จะเปลี่ยนเฉพาะในรูปแบบไวยากรณ์ ปัญหาหลักคือวิธีการดึงข้อมูล สิ่งที่คุณใช้เป็นวิธีการรับข้อมูลแบบซิงโครนัส วิธีนี้ใช้ไม่ได้ในทุกกรณี สิ่งที่คุณควรใช้คือวิธีดึงข้อมูลแบบอะซิงโครนัส ด้วยวิธีนี้คุณต้องขอข้อมูลผ่าน API และรอให้ข้อมูลตอบกลับ คุณสามารถบรรลุนี้กับเซสชั่น URL Alamofireและบุคคลที่สามเช่นห้องสมุด ด้านล่างนี้คือรหัสสำหรับวิธีเซสชัน URL

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

0
{
    "User":[
      {
        "FirstUser":{
        "name":"John"
        },
       "Information":"XY",
        "SecondUser":{
        "name":"Tom"
      }
     }
   ]
}

ถ้าฉันสร้างโมเดลโดยใช้ json ก่อนหน้าโดยใช้ลิงค์นี้ [บล็อก]: http://www.jsoncafe.comเพื่อสร้างโครงสร้าง Codable หรือรูปแบบใดก็ได้

แบบ

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)
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.