Search code examples
jsonswiftstringparsing

How to get string response values while JSON parsing in swift


I am getting response like this in postman from backend

"{\"errorCode\":0,\"status\":\"success\",\"message\":\"User Undertaking\",\"utID\":\"f6W1XDIzD2lmNL81kR/D2Q==\",\"showUserUndertaking\":true,\"htmlDecription\":\"\\u003cp class=\\u0022s1\\u0022 style=\\u0022margin: 0pt; padding: 0px 0px 0px 2pt; text-indent: 0pt; font-family: Calibri, sans-serif; text-decoration-line: underline; font-size: 20pt; line-height: 24pt; text-align: center;\\u0022\\u003e\\u003cspan style=\\u0022font-family: \\u0026quot;Times New Roman\\u0026quot;; background-color: rgb(99, 99, 99);\\u0022\\u003ePARENT/STUDENT ACKNOWLEDGEMENT FORM\\u003c/span\\u003e\\u003c/p\\u003e\\u003cp style=\\u0022margin: 0pt; padding: 5pt 0px 0px; text-indent: 0pt; font-family: Calibri, sans-serif; font-size: 13pt;\\u0022\\u003e\\u003cbr style=\\u0022margin: 0px; padding: 0px;\\u0022\\u003e\\u003c/p\\u003e\\u003cp style=\\u0022margin: 0pt; padding: 0px 0px 0px 9pt; text-indent: 0pt; font-family: Calibri, sans-serif; font-size: 13pt;\\u0022\\u003e\\u003ca href=\\u0022http://www.christinak12.org/StudentManual\\u0022 target=\\u0022_blank\\u0022 style=\\u0022margin: 0px; padding: 0px; color: black; font-size: 13pt;\\u0022\\u003e\\u003cu\\u003eChristina School District\\u0027\\u003c/u\\u003es \\u0022Student Manual: Responsibilities, Expectations, Rights \\u0026amp; Resources\\u0022 is available online on the District\\u0027s website at\\u0026nbsp;\\u003c/a\\u003e\\u003ca href=\\u0022http://www.christinak12.org/StudentManual\\u0022 target=\\u0022_blank\\u0022 style=\\u0022margin: 0px; padding: 0px; color: black; text-decoration-line: underline; font-size: 13pt;\\u0022\\u003ewww.christinak12.org/StudentManual\\u003c/a\\u003e\\u003c/p\\u003e\\u003cp style=\\u0022margin: 0pt; padding: 14pt 0px 0px 9pt; text-indent: 0pt; font-family: Calibri, sans-serif; font-size: 13pt; text-align: justify;\\u0022\\u003ePlease review the Student Manual with your student. His/her classroom teacher or building administrator will also discuss this document at school.\\u003c/p\\u003e\\u003cp style=\\u0022margin:

now i need these values

here how to get these values

utID

showUserUndertaking

htmlDecription

here I am getting the same above postman response in

if let jsonString = String(data: data, encoding: .utf8) {
    print("Received JSON data: \(jsonString)")    
}

but not getting inside

   print("errorCode: \(response.errorCode)")
   print("status: \(response.status)")

code:

func termsandCondition() {
    let baseURL = APIConstants.basePath
    let urlStr = "Undertaking?"
    var strKey    = String()
    strKey       += strKey.keyPath()
    
    let key = "key=\(strKey)"
    
    let SchCode = "&SchCode=\(strSchoolCodeSender)"
    let UserID = "&UserID=\(intUserID.toString)"//intUserID.toString
    let UserType = "&UserType=\(strPassword)"//intUserType.toString
    
    guard let url = URL(string: baseURL + urlStr + key + SchCode + UserID + UserType) else {
        print("Invalid URL")
        return
    }
    print("the url is:......\(url)")
    
    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else {
            print(error?.localizedDescription ?? "")
            return
        }
        
        if let jsonString = String(data: data, encoding: .utf8) {
            print("Received JSON data: \(jsonString)")
            
        } else {
            print("Unable to decode JSON data")
        }
        
        
        do {
            let decoder = JSONDecoder()
            let response = try decoder.decode(UserUndertakingResponse.self, from: data)
            print("errorCode: \(response.errorCode)")
            print("status: \(response.status)")
            // Similarly, access other properties...
        } catch {
            print("Error decoding JSON: \(error)")
        }
        
    }.resume()
}

error:

Error decoding JSON....: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No string key for value in object around line 1, column 1." UserInfo={NSDebugDescription=No string key for value in object around line 1, column 1., NSJSONSerializationErrorIndex=1})))

EDIT: this is the model

func termsandCondition() { let baseURL = APIConstants.basePath let urlStr = "User/UserUndertaking?" var strKey = String() strKey += strKey.keyPath()

let key = "key=\(strKey)"

let SchCode = "&SchCode=\(strSchoolCodeSender)"
let UserID = "&UserID=\(intUserID.toString)"//intUserID.toString
let UserType = "&UserType=\(intUserType)"//intUserType.toString

guard let url = URL(string: baseURL + urlStr + key + SchCode + UserID + UserType) else {
    print("Invalid URL")
    return
}
print("the url is:......\(url)")

var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")

URLSession.shared.dataTask(with: request) { data, response, error in
    guard let data = data, error == nil else {
        print(error?.localizedDescription ?? "")
        return
    }

    if let jsonString = String(data: data, encoding: .utf8) {
        print("Received JSON data: \(jsonString)")
        
    } else {
        print("Unable to decode JSON data")
    }
    print("-------------------------------")
    print(data.map { String(format: "%02hhx", $0) }.joined())
    print("-------------------------------")
    
    let backendJsonStr = String(data: data, encoding: .utf8)
    
    if let data = backendJsonStr?.data(using: .utf8) {
        do {
            // Deserialize the data into a JSON object
            if let json = try JSONSerialization.jsonObject(with: data, options: []) as? Dictionary<String, Any> {
                // Use the json dictionary here
                print("json response........ \(json)")
            } else {
                print("Failed to convert JSON string to dictionary.")
            }
        } catch {
            print("Error deserializing JSON: \(error)")
        }
    } else {
        print("Failed to convert string to data.")
    }

}.resume()

}

error:

Error deserializing JSON: Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set. around line 1, column 0." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set. around line 1, column 0., NSJSONSerializationErrorIndex=0}

struct UserUndertakingResponse: Codable {
    let errorCode: Int
    let status: String
    let message: String
    let utID: String
    let showUserUndertaking: Bool
    let htmlDescription: String // This will hold the HTML content as a string
    
    enum CodingKeys: String, CodingKey {
        case errorCode, status, message, utID, showUserUndertaking, htmlDescription
    }
}

Solution

  • After seeing the raw response, you have JSON Stringified, ie JSON String inside JSON.

    So you need to decode first the JSON as it was a String, then decode according to your own model:

    do {
        let string = try JSONDecoder().decode(String.self, from: data)
        let object = try JSONDecoder().decode(UserUndertakingResponse.self, from: Data(string.utf8))
        print(object)
    } catch {
        print("Error decoding JSON: \(error)")
    
    }
    

    Your response has a typo, either it's fixed on the API, or you can force the decoding: It's written "htmlDecription" instead of "htmlDescription" (missing an "s").

    enum CodingKeys: String, CodingKey {
        case errorCode, status, message, utID, showUserUndertaking
        case htmlDescription = "htmlDecription"
    }
    

    Now, unrelated to your issue, but name your variables starting with a lowercase:

    let SchCode = "&SchCode=\(strSchoolCodeSender)"
    

    -->

    let schCode = "&SchCode=\(strSchoolCodeSender)"
    

    Also, for constructing the URL, I'd suggest to use URLQueryItem:

    let baseURL = APIConstants-basePath.com + "/Undertaking"
    var components = URLComponents(string: baseURL)
    components?.queryItems = [URLQueryItem(name: "SchCode", value: strSchoolCodeSender),
                              URLQueryItem(name: "UserID", value: intUserID.toString)
                              ...
        ]
    guard let url = components.url else { 
        print("Invalid URL")
        return
    }