จะแยกวิเคราะห์การตอบสนอง JSON จาก Alamofire API ใน Swift ได้อย่างไร


125

ฉันเขียนโค้ดต่อไปนี้และฉันได้รับการตอบกลับใน JSON ด้วย แต่ประเภทของ JSON คือ "AnyObject" และฉันไม่สามารถแปลงเป็น Array เพื่อให้ฉันสามารถใช้สิ่งนั้นได้

Alamofire.request(.POST, "MY URL", parameters:parameters, encoding: .JSON) .responseJSON
{
    (request, response, JSON, error) in

    println(JSON?)
}

ฉันไม่ได้ลงคะแนนคำถามของคุณ แต่ฉันคิดว่าเป็นเพราะการแยกวิเคราะห์ JSON เป็นหัวข้อที่กว้างเกินไปที่จะให้คำตอบที่ชัดเจนและตรงประเด็น ลองใช้ไลบรารีชื่อSwiftyJSONนี้
Isuru

@ อิซุรุไม่เป็นไร! ฉันเคยเห็นห้องสมุดนั้น แต่ฉันใช้ Alamofire! แต่คุณช่วยส่งโค้ดตัวอย่างที่คุณใช้ SwiftyJson มาให้ฉันได้ไหม มีรหัสไม่ได้ผลสำหรับฉัน!
พัฒนา

ฉันใช้ SwiftyJSON ร่วมกับ Alamofire ด้วย let data = JSONValue(JSON!)ฉันเพียงแค่ผ่านการตอบสนองในการเช่นนี้ จากนั้นฉันสามารถดึงค่าแบบนี้data["Id"]ได้ เอกสาร SwiftyJSON แสดงตัวอย่างวิธีการดึงค่าเหล่านั้นในประเภทที่ต้องการ คุณได้รับข้อผิดพลาดอะไรกันแน่?
Isuru

คำตอบ:


160

คำตอบสำหรับ Swift 2.0 Alamofire 3.0 ควรมีลักษณะดังนี้:

Alamofire.request(.POST, url, parameters: parameters, encoding:.JSON).responseJSON
{ response in switch response.result {
                case .Success(let JSON):
                    print("Success with JSON: \(JSON)")

                    let response = JSON as! NSDictionary

                    //example if there is an id
                    let userId = response.objectForKey("id")!

                case .Failure(let error):
                    print("Request failed with error: \(error)")
                }
    }

https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md

อัปเดตสำหรับ Alamofire 4.0 และ Swift 3.0:

Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
            .responseJSON { response in
                print(response)
//to get status code
                if let status = response.response?.statusCode {
                    switch(status){
                    case 201:
                        print("example success")
                    default:
                        print("error with response status: \(status)")
                    }
                }
//to get JSON return value
            if let result = response.result.value {
                let JSON = result as! NSDictionary
                print(JSON)
            }

        }

13
คุณเข้าใจเนื้อหาจริงของ JSON ได้อย่างไร? วัตถุประเภทนี้คืออะไร? การออกแบบและเอกสารนั้นคลุมเครือมากฉันคิดไม่ออกและไม่พบตัวอย่างใด ๆ บนอินเทอร์เน็ต ...
Alex Worden

ฉันเพิ่มสองสามบรรทัดในคำตอบที่น่าจะช่วยได้
Joseph Geraghty

@JosephGeraghty มีพารามิเตอร์การเข้ารหัสส่งผลให้คอมไพเลอร์บอกว่ามีการเรียกอาร์กิวเมนต์พิเศษ ... มีความคิดอย่างไร
amariduran

@ jch-duran ไม่เป็นบวก แต่ฉันจำได้ไม่ชัดว่าวิ่งเข้าไปในสิ่งที่คล้ายกันในขณะที่กลับมา ฉันคิดว่ามันมีส่วนเกี่ยวข้องกับการที่ไลบรารีไม่ได้รับการอัปเดตหรืออาจไม่เป็นปัจจุบันสำหรับเวอร์ชันที่รวดเร็ว การตรวจสอบให้แน่ใจว่าคุณใช้เวอร์ชันล่าสุดอาจช่วยได้
Joseph Geraghty

1
@AlexWorden เห็นด้วยหน้านี้ช่วยฉันตอบคำถามเหล่านั้นและเป็นทางออกที่ดี: github.com/SwiftyJSON/SwiftyJSON
iljn

31

ดังที่กล่าวไว้ข้างต้นคุณสามารถใช้ไลบรารี SwiftyJSON และรับค่าของคุณเหมือนที่ฉันได้ทำไว้ด้านล่าง

Alamofire.request(.POST, "MY URL", parameters:parameters, encoding: .JSON) .responseJSON
{
    (request, response, data, error) in

var json = JSON(data: data!)

       println(json)   
       println(json["productList"][1])                 

}

รายการผลิตภัณฑ์ json ของฉันส่งคืนจากสคริปต์

{ "productList" :[

{"productName" : "PIZZA","id" : "1","productRate" : "120.00","productDescription" : "PIZZA AT 120Rs","productImage" : "uploads\/pizza.jpeg"},

{"productName" : "BURGER","id" : "2","productRate" : "100.00","productDescription" : "BURGER AT Rs 100","productImage" : "uploads/Burgers.jpg"}    
  ]
}

เอาต์พุต:

{
  "productName" : "BURGER",
  "id" : "2",
  "productRate" : "100.00",
  "productDescription" : "BURGER AT Rs 100",
  "productImage" : "uploads/Burgers.jpg"
}

ฉันพยายามใช้สิ่ง SwiftyJson หลังจากติดตั้ง แต่มีข้อผิดพลาด 300 ข้อในไฟล์ SwiftyJson มีใครประสบปัญหาหรือไม่ i 'โดยใช้ Xcode เวอร์ชัน 6.2, ios เวอร์ชัน 8.1 ,ocoaPods 36 ตามที่ระบุไว้ในเอกสาร [github] ( github.com/SwiftyJSON/SwiftyJSON )
Sashi

2
เพื่อน. มีข้อผิดพลาดอย่างไร ถามคำถามแยกต่างหากและให้รายละเอียดบางอย่าง SwiftyJSON สวยงามราวกับเวทมนตร์ ใช้มันถ้าเป็นไปได้
Zia

จริงๆแล้วควรจะแปลงสตริง json เป็นวัตถุที่เป็นคอนกรีตอย่างรวดเร็วเพื่อให้คุณสามารถใช้งานได้อย่างเป็นธรรมชาติ การเข้าถึงฟิลด์ด้วยชื่อสตริงนั้นไร้สาระและมีแนวโน้มที่จะเกิดข้อผิดพลาด
The Muffin Man

26

Swift 3, Alamofire 4.4 และ SwiftyJSON:

Alamofire.request(url, method: .get)
  .responseJSON { response in
      if response.data != nil {
        let json = JSON(data: response.data!)
        let name = json["people"][0]["name"].string
        if name != nil {
          print(name!)
        }
      }
  }

ที่จะแยกวิเคราะห์อินพุต JSON นี้:

{
  people: [
    { name: 'John' },
    { name: 'Dave' }
  ]
}

นอกจากนี้ยังมีปลั๊กอินเฉพาะ Alamofire Swifty-JSON ที่ขจัดความจำเป็นในJSON()การแปลงอย่างชัดเจน: github.com/SwiftyJSON/Alamofire-SwiftyJSON
Robin Macharg

สิ่งนี้ช่วยฉันได้ แต่ฉันมีปัญหากับเมธอด JSON เพราะพ่น Exception
iGhost

24

ฉันพบคำตอบใน GitHub สำหรับSwift2

https://github.com/Alamofire/Alamofire/issues/641

Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
    .responseJSON { request, response, result in
        switch result {
        case .Success(let JSON):
            print("Success with JSON: \(JSON)")

        case .Failure(let data, let error):
            print("Request failed with error: \(error)")

            if let data = data {
                print("Response data: \(NSString(data: data, encoding: NSUTF8StringEncoding)!)")
            }
        }
    }

3
นี่เป็นเวอร์ชันที่ถูกต้องสำหรับการแยกวิเคราะห์ Swift 2.0 + Alamofire JSON
Saqib Omer

5
อืมฉันยังคงได้รับความล้มเหลวในการสร้างข้อความแสดงข้อผิดพลาด: '(_, _, _) -> Void' ไม่สามารถแปลงเป็น 'Response <AnyObject, NSError> -> Void'
อเล็กซ์

@alex ดูคำตอบนี้สำหรับสิ่งที่ฉันใช้ในการแก้ไข
โจเซฟ

ขอบคุณมาก ! คุณไม่รู้หรอกว่าฉันพยายามแสดงข้อความตอบกลับจากเซิร์ฟเวอร์กี่ครั้งผู้ช่วยชีวิต!
thibaut noah

17

ฉันไม่ใช่ผู้เชี่ยวชาญ JSON หรือผู้เชี่ยวชาญด้าน Swift แต่สิ่งต่อไปนี้ใช้ได้ผลสำหรับฉัน :) ฉันได้แยกโค้ดจากแอปปัจจุบันของฉันและเปลี่ยนเฉพาะ "MyLog เป็น println" และเยื้องด้วยช่องว่างเพื่อให้แสดงเป็นบล็อกรหัส (หวังว่าฉันจะไม่ทำลายมัน)

func getServerCourseVersion(){

    Alamofire.request(.GET,"\(PUBLIC_URL)/vtcver.php")
        .responseJSON { (_,_, JSON, _) in
          if let jsonResult = JSON as? Array<Dictionary<String,String>> {
            let courseName = jsonResult[0]["courseName"]
            let courseVersion = jsonResult[0]["courseVersion"]
            let courseZipFile = jsonResult[0]["courseZipFile"]

            println("JSON:    courseName: \(courseName)")
            println("JSON: courseVersion: \(courseVersion)")
            println("JSON: courseZipFile: \(courseZipFile)")

          }
      }
}

หวังว่านี่จะช่วยได้

แก้ไข:

สำหรับการอ้างอิงนี่คือสิ่งที่สคริปต์ PHP ของฉันส่งคืน:

[{"courseName": "Training Title","courseVersion": "1.01","courseZipFile": "101/files.zip"}]

นี่ควรเป็นคำตอบที่เลือกแม้ว่าคุณอาจต้องการอัปเดตเนื่องจาก Alamofire ได้อัปเดตวิธีการของพวกเขาเล็กน้อย
Snymax

10

รวดเร็ว 3

pod 'Alamofire', '~> 4.4'
pod 'SwiftyJSON'

File json format:
{
    "codeAd": {
        "dateExpire": "2017/12/11",
        "codeRemoveAd":"1231243134"
        }
}

import Alamofire
import SwiftyJSON
    private func downloadJson() {
        Alamofire.request("https://yourlinkdownloadjson/abc").responseJSON { response in
            debugPrint(response)

            if let json = response.data {
                let data = JSON(data: json)
                print("data\(data["codeAd"]["dateExpire"])")
                print("data\(data["codeAd"]["codeRemoveAd"])")
            }
        }
    }

2

ฉันพบวิธีแปลง response.result.value (ภายใน Alamofire responseJSON closure) เป็นรูปแบบ JSON ที่ฉันใช้ในแอปของฉัน

ฉันใช้ Alamofire 3 และ Swift 2.2

นี่คือรหัสที่ฉันใช้:

    Alamofire.request(.POST, requestString,
                      parameters: parameters,
                      encoding: .JSON,
                      headers: headers).validate(statusCode: 200..<303)
                                       .validate(contentType: ["application/json"])
                                       .responseJSON { (response) in
        NSLog("response = \(response)")

        switch response.result {
        case .Success:
            guard let resultValue = response.result.value else {
                NSLog("Result value in response is nil")
                completionHandler(response: nil)
                return
            }

            let responseJSON = JSON(resultValue)

            // I do any processing this function needs to do with the JSON here

            // Here I call a completionHandler I wrote for the success case
        break
        case .Failure(let error):
            NSLog("Error result: \(error)")
            // Here I call a completionHandler I wrote for the failure case
            return
        }

2

ฉันมักจะใช้ไลบรารีGlossเพื่อทำให้เป็นอนุกรมหรือแยกส่วน JSON ใน iOS ตัวอย่างเช่นฉันมี JSON ที่มีลักษณะดังนี้:

{"ABDC":[{"AB":"qwerty","CD":"uiop"}],[{"AB":"12334","CD":"asdf"}]}

ก่อนอื่นฉันสร้างโมเดลอาร์เรย์ JSON ใน Gloss struct:

Struct Struct_Name: Decodable {
   let IJ: String?
   let KL: String?
   init?(json: JSON){
       self.IJ = "AB" <~~ json
       self.KL = "CD" <~~ json
   }
}

จากนั้นใน Alamofire responseJSON ฉันทำสิ่งต่อไปนี้:

Alamofire.request(url, method: .get, paramters: parametersURL).validate(contentType: ["application/json"]).responseJSON{ response in
 switch response.result{
   case .success (let data):
    guard let value = data as? JSON,
       let eventsArrayJSON = value["ABDC"] as? [JSON]
    else { fatalError() }
    let struct_name = [Struct_Name].from(jsonArray: eventsArrayJSON)//the JSON deserialization is done here, after this line you can do anything with your JSON
    for i in 0 ..< Int((struct_name?.count)!) {
       print((struct_name?[i].IJ!)!)
       print((struct_name?[i].KL!)!)
    }
    break

   case .failure(let error):
    print("Error: \(error)")
    break
 }
}

ผลลัพธ์จากโค้ดด้านบน:

qwerty
uiop
1234
asdf

2

สวิฟต์ 5

class User: Decodable {

    var name: String
    var email: String
    var token: String

    enum CodingKeys: String, CodingKey {
        case name
        case email
        case token
    }

    public required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.email = try container.decode(String.self, forKey: .email)
        self.token = try container.decode(String.self, forKey: .token)
    }
}

Alamofire API

    Alamofire.request("url.endpoint/path", method: .get, parameters: params, encoding: URLEncoding.queryString, headers: nil)
     .validate()
     .responseJSON { response in

        switch (response.result) {

            case .success( _):

            do {
                let users = try JSONDecoder().decode([User].self, from: response.data!)
                print(users)

            } catch let error as NSError {
                print("Failed to load: \(error.localizedDescription)")
            }

             case .failure(let error):
                print("Request error: \(error.localizedDescription)")
         }

1

สิ่งนี้สร้างด้วย Xcode 10.1 และ Swift 4

ส่วนผสมที่ลงตัว "Alamofire" (4.8.1) และ "SwiftyJSON" (4.2.0) ก่อนอื่นคุณควรติดตั้งพ็อดทั้งสอง

pod 'Alamofire' และ pod 'SwiftyJSON'

การตอบสนองของเซิร์ฟเวอร์ในรูปแบบ JSON:

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip;q=1.0, compress;q=0.5", 
    "Accept-Language": "en;q=1.0", 
    "Host": "httpbin.org", 
    "User-Agent": "AlamoFire TEST/1.0 (com.ighost.AlamoFire-TEST; build:1; iOS 12.1.0) Alamofire/4.8.1"
  }, 
  "origin": "200.55.140.181, 200.55.140.181", 
  "url": "https://httpbin.org/get"
}

ในกรณีนี้ฉันต้องการพิมพ์ข้อมูล "โฮสต์": "โฮสต์": "httpbin.org"

Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
        switch response.result {
        case .success:
            print("Validation Successful)")

            if let json = response.data {
                do{
                    let data = try JSON(data: json)
                    let str = data["headers"]["Host"]
                    print("DATA PARSED: \(str)")
                }
                catch{
                print("JSON Error")
                }

            }
        case .failure(let error):
            print(error)
        }
    }

รักษาความสงบและมีความสุขรหัส 😎


0

ใน 5 อย่างรวดเร็วเราชอบใช้ typealias เพื่อให้เสร็จสมบูรณ์ Typlealias ไม่มีอะไรใช้เพียงแค่ทำความสะอาดโค้ด

typealias response = (Bool,Any?)->()


static func postCall(_ url : String, param : [String : Any],completion : @escaping response){
    Alamofire.request(url, method: .post, parameters: param, encoding: JSONEncoding.default, headers: [:]).responseJSON { (response) in

        switch response.result {
           case .success(let JSON):
               print("\n\n Success value and JSON: \(JSON)")

           case .failure(let error):
               print("\n\n Request failed with error: \(error)")

           }
    }
}

-10
 pod 'Alamofire'
 pod 'SwiftyJSON'
 pod 'ReachabilitySwift'



import UIKit
import Alamofire
import SwiftyJSON
import SystemConfiguration

class WebServiceHelper: NSObject {

    typealias SuccessHandler = (JSON) -> Void
    typealias FailureHandler = (Error) -> Void

    // MARK: - Internet Connectivity

    class func isConnectedToNetwork() -> Bool {

        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)

        guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }) else {
            return false
        }

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

        let isReachable = flags.contains(.reachable)
        let needsConnection = flags.contains(.connectionRequired)

        return (isReachable && !needsConnection)
    }

    // MARK: - Helper Methods

    class func getWebServiceCall(_ strURL : String, isShowLoader : Bool, success : @escaping SuccessHandler, failure : @escaping FailureHandler)
    {
        if isConnectedToNetwork() {

            print(strURL)

            if isShowLoader == true {

                AppDelegate.getDelegate().showLoader()
            }

            Alamofire.request(strURL).responseJSON { (resObj) -> Void in

                print(resObj)

                if resObj.result.isSuccess {
                    let resJson = JSON(resObj.result.value!)

                    if isShowLoader == true {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    debugPrint(resJson)
                    success(resJson)
                }
                if resObj.result.isFailure {
                    let error : Error = resObj.result.error!

                    if isShowLoader == true {
                        AppDelegate.getDelegate().dismissLoader()
                    }
                    debugPrint(error)
                    failure(error)
                }
            }
        }else {


            CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
        }
    }

    class func getWebServiceCall(_ strURL : String, params : [String : AnyObject]?, isShowLoader : Bool, success : @escaping SuccessHandler,  failure :@escaping FailureHandler){
        if isConnectedToNetwork() {

            if isShowLoader == true {
                AppDelegate.getDelegate().showLoader()
            }


            Alamofire.request(strURL, method: .get, parameters: params, encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: {(resObj) -> Void in

                print(resObj)

                if resObj.result.isSuccess {
                    let resJson = JSON(resObj.result.value!)

                    if isShowLoader == true {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    success(resJson)
                }
                if resObj.result.isFailure {
                    let error : Error = resObj.result.error!

                    if isShowLoader == true {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    failure(error)
                }

            })
        }
    else {

            CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
    }

    }



    class func postWebServiceCall(_ strURL : String, params : [String : AnyObject]?, isShowLoader : Bool, success : @escaping SuccessHandler, failure :@escaping FailureHandler)
    {
        if isConnectedToNetwork()
        {

            if isShowLoader == true
            {
                AppDelegate.getDelegate().showLoader()
            }

            Alamofire.request(strURL, method: .post, parameters: params, encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: {(resObj) -> Void in

                print(resObj)

                if resObj.result.isSuccess
                {
                    let resJson = JSON(resObj.result.value!)

                    if isShowLoader == true
                    {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    success(resJson)
                }

                if resObj.result.isFailure
                {
                    let error : Error = resObj.result.error!

                    if isShowLoader == true
                    {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    failure(error)
                }
            })
        }else {
            CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
        }
    }


    class func postWebServiceCallWithImage(_ strURL : String, image : UIImage!, strImageParam : String, params : [String : AnyObject]?, isShowLoader : Bool, success : @escaping SuccessHandler, failure : @escaping FailureHandler)
    {
        if isConnectedToNetwork() {
            if isShowLoader == true
            {
                AppDelegate.getDelegate().showLoader()
            }

            Alamofire.upload(
                multipartFormData: { multipartFormData in
                    if let imageData = UIImageJPEGRepresentation(image, 0.5) {
                        multipartFormData.append(imageData, withName: "Image.jpg")
                    }

                    for (key, value) in params! {

                        let data = value as! String

                        multipartFormData.append(data.data(using: String.Encoding.utf8)!, withName: key)
                        print(multipartFormData)
                    }
                },
                to: strURL,
                encodingCompletion: { encodingResult in
                    switch encodingResult {
                    case .success(let upload, _, _):
                        upload.responseJSON { response in
                            debugPrint(response)
                            //let datastring = String(data: response, encoding: String.Encoding.utf8)
                           // print(datastring)
                        }
                    case .failure(let encodingError):
                        print(encodingError)
                        if isShowLoader == true
                        {
                            AppDelegate.getDelegate().dismissLoader()
                        }

                        let error : NSError = encodingError as NSError
                        failure(error)
                    }

                    switch encodingResult {
                    case .success(let upload, _, _):
                        upload.responseJSON { (response) -> Void in

                            if response.result.isSuccess
                            {
                                let resJson = JSON(response.result.value!)

                                if isShowLoader == true
                                {
                                    AppDelegate.getDelegate().dismissLoader()
                                }

                                success(resJson)
                            }

                            if response.result.isFailure
                            {
                                let error : Error = response.result.error! as Error

                                if isShowLoader == true
                                {
                                    AppDelegate.getDelegate().dismissLoader()
                                }

                                failure(error)
                            }

                        }
                    case .failure(let encodingError):
                        if isShowLoader == true
                        {
                            AppDelegate.getDelegate().dismissLoader()
                        }

                        let error : NSError = encodingError as NSError
                        failure(error)
                    }
                }
            )
        }
        else
        {
            CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
        }
    }

}


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


Call Method


let aParams : [String : String] = [
                "ReqCode" : Constants.kRequestCodeLogin,
                ]

            WebServiceHelper.postWebServiceCall(Constants.BaseURL, params: aParams as [String : AnyObject]?, isShowLoader: true, success: { (responceObj) in


                if "\(responceObj["RespCode"])" != "1"
                {
                    let alert = UIAlertController(title: Constants.kAppName, message: "\(responceObj["RespMsg"])", preferredStyle: UIAlertControllerStyle.alert)
                    let OKAction = UIAlertAction(title: "OK", style: .default) { (action:UIAlertAction!) in
                    }
                    alert.addAction(OKAction)
                    self.present(alert, animated: true, completion: nil)
                }
                else
                {
                    let aParams : [String : String] = [
                        "Password" : self.dictAddLogin[AddLoginConstants.kPassword]!,
                        ]
                    CommonMethods.saveCustomObject(aParams as AnyObject?, key: Constants.kLoginData)

                }
                }, failure:
                { (error) in

                    CommonMethods.showAlertWithError(Constants.kALERT_TITLE_Error, strMessage: error.localizedDescription,withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
            })
        }

6
คำอธิบายว่าโค้ดทั้งหมดนี้มีประโยชน์อะไร
peter.swallow
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.