I am receiving text from a web socket. And I want to convert the text to JSON.
Text received from the socket:
{'id': 920, 'location': {'lat': 11.0368754733495, 'lon': -47.203396772120247}}
Tried this:
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
print("got some text: \(text)")
let data = Data(text.utf8)
do {
// make sure this JSON is in the format we expect
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
// try to read out a string array
if let id = json["id"] as? Int {
print(id)
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
But I am getting Failed to load: The data couldn’t be read because it isn’t in the correct format. error.
This is not valid JSON. The keys must be wrapped in double quotes.
You can replace the single quotes with double quotes on the fly
let data = Data(text.replacingOccurrences(of: "\'", with: "\"").utf8)
Side note:
Never print .localizedDescription in JSONSerialization/JSONDecoder catch blocks. And bridge casting to NSError is redundant
catch {
print("Failed to load:", error)
}
Related
This question already has answers here:
Decoding Error -- Expected to decode Dictionary<String, Any> but found an array instead
(2 answers)
Closed 1 year ago.
I'm working on a creative project, and I'm trying to decode content from an API database using Swift's JSONDecoder() function. I've built my structs, a getData() function, and I've set up a do-try-catch for the JSONDecoder() function. I'm having difficulty understanding what I'm doing to get the error I'm getting.
Here are my structs:
struct Response: Codable {
let foundRecipes: [Recipe]
let foundIngredients: [Ingredient]
}
struct Recipe: Codable {
let id: Int
let title: String
let image: String
let imageType: String
let usedIngredientCount: Int
let missedIngredientCount: Int
let missedIngredients: [Ingredient]
let usedIngredients: [Ingredient]
let unusedIngredients: [Ingredient]
let likes: Int
}
struct Ingredient: Codable {
let id: Int
let amount: Int
let unit: String
let unitLong: String
let unitShort: String
let aisle: String
let name: String
let original: String
let originalString: String
let origianalName: String
let metaInformation: [String]
let meta: [String]
let image: String
}
Here's my getData() function:
func getData(from url: String) {
URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("something went wrong.")
return
}
var result: Response?
do {
result = try JSONDecoder().decode(Response.self, from: data)
}
catch {
print("")
print(String(describing: error)) // Right here is where the error hits.
}
guard let json = result else {
return
}
print(json.foundRecipes)
}).resume()
}
Here's a link to the API's documentation. The URL I'm calling in getData() links to the same structure of search as shown in their example: https://spoonacular.com/food-api/docs#Search-Recipes-by-Ingredients — and here's a screenshot of the url results for the exact search I'm working on: https://imgur.com/a/K3Rn9SZ
And finally, here's the full error that I'm catching:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
My understanding of this error is that it's saying I told the JSONDecoder() to look for a Dictionary of <String, Any>, but it's at the link and only seeing an array. I'm confused, because I don't know where it thinks I'm providing a dictionary. Where am I screwing up? Not looking for specific code changes, just some guidance on what I'm missing.
Thanks in advance :)
As you can see in your image of the API data and in the API documentation you linked to, the API is returning an array (in the documentation, for example, you can see that it is surrounded by [...]). In fact, it looks like the API returns an array of Recipe.
So, you can change your decoding call to this:
var result: [Recipe]?
do {
result = try JSONDecoder().decode([Recipe].self, from: data)
print(result)
} catch {
print(error)
}
Perhaps your idea for Response came from somewhere else, but the keys foundRecipes or foundIngredients don't show up in this particular API call.
Also, thanks to #workingdog's for a useful comment about changing amount to a Double instead of an Int in your model.
What could be causing this error?
All of a sudden out of nowhere I started getting the error below. I have reinstalled the cocoapod, cleaned the build folder, and reinstalled the app already and none of that has fixed the error.
ERROR: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
CODE:
let recoverUrl = "http://www.website.com/recover.php?email=\(emailData)&local=application"
let urlEncodedString = recoverUrl.replacingOccurrences(of: " ", with: "%20")
parseRecover(url: urlEncodedString)
//////////////
func parseRecover(url : String){ AF.request(url).responseJSON(completionHandler: { response in self.parseData(JSONData: response.data!) }) }
func parseData(JSONData : Data){
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONObject
if let recoverJSON = readableJSON["Recover"] as? [JSONObject] {
for i in 0..<recoverJSON.count {
let JSON = recoverJSON[i]
let status = JSON["status"] as! String
let message = JSON["message"] as! String
if status == "Error" {self.Alert01("\(message)")}
else if status == "Success" { self.Alert02("\(message)") }
}}}
catch { print(error) }
}
ERROR IS OCCURING AT:
func parseRecover(url : String){ AF.request(url).responseJSON(completionHandler: { response in self.parseData(JSONData: response.data!) }) }
There's no guarantee that a response has data, so force unwrapping the value can lead to crashes. I suggest you create Decodable types to parse your responses and use Alamofire's responseDecodable method to handle your responses.
Additionally, even if you don't adopt Decodable, responseJSON already parses your response Data using JSONSerialization, so you can just access the response.result to see the output.
SOLVED: The issue was within my php file. I was using $_GET[''] and it should have been a $_POST[''] or $_REQUEST['']
struct KOTextPrompt: Codable {
let prompt: String
let response: String
}
I have a very simple struct that is Codable. I'm been trying to pass this as a parameter using Alamofire and got a crash
2019-07-31 14:52:00.894242-0700 Kirby[8336:1685359] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (__SwiftValue)'
I tried printing the code below and got "false". What am I doing wrong?
let gg = KOTextPrompt(prompt: "testprompt", response: "testresponse")
print(JSONSerialization.isValidJSONObject(gg))
The issue here is that gg which is an instance of KOTextPromptit is not a valid JSON object. You need to encode your struct:
struct KOTextPrompt: Codable {
let prompt, response: String
}
let gg = KOTextPrompt(prompt: "testprompt", response: "testresponse")
do {
let data = try JSONEncoder().encode(gg)
print("json string:", String(data: data, encoding: .utf8) ?? "")
let jsonObject = try JSONSerialization.jsonObject(with: data)
print("json object:", jsonObject)
} catch { print(error) }
This will print
json string: {"response":"testresponse","prompt":"testprompt"}
json
object: {
prompt = testprompt;
response = testresponse; }
can you help me,
I'm facing an issue if the JSON came with multilines like this
"{\"groupId\":\"58\",\"chat\":\"send 2lines\nsecondline\"}"
I'm taking the response from server and convert it with this function
let dataDic = self.convertToDictionary(text: (remoteMessage.appData["message"]! as AnyObject) as! String)
print(dataDic!)
and this is my function
func convertToDictionary(text: String) -> [String: AnyObject]? {
if let data = text.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject]
return json
} catch {
print(error.localizedDescription)
}
}
return nil
}
but the problem came if the code have multilines because it's put \n in the return and
it gives me
The data couldn’t be read because it isn’t in the correct format
Error Domain=NSCocoaErrorDomain Code=3840 "Unescaped control character around character 145." UserInfo={NSDebugDescription=Unescaped control character around character 145.}
You should put an extra "\" before "\n", before parsing your JSON. Try using "replacingOccurencesOf" function.
That way your JSON is formatted before parsing.
I am trying to create JSON Web Token using JSONSerialization class, Swift 3 and Xcode 8.1, but my project fails to build with error:
Command failed due to signal: Segmentation fault 11.
Anyone knows why my code is not correct?
If I comment out this code from the project, the project builds.
let customerError = "Custom Error"
enum headerError: Error {
case customerError
}
let headerJWT: [Dictionary] = ["alg":"RS256","typ":"JWT"]
//Convert headerJWT to Data
do {
let headerJWTData: Data = try? JSONSerialization.data(withJSONObject:headerJWT,options: JSONSerialization.WritingOptions.prettyPrinted)
} catch headerError.customerError {
print("could not make data")
}
//Convert headerData to string utf8
do {
let headerJWTString = try String(data: headerJWTData,encoding:String.Encoding.utf8) as! String
} catch {
print("string could not be created")
}
//Convert headerJWTString to base64EncodedString
do {
let headerJWTBase64 = try Data(headerJWTString.utf8).base64EncodedString()
} catch {
"base64 could not be created"
}
Once you create the Data from using JSONSerialization, you simply use the method from Data to get a base64 encoded string.
let headerJWT: [Dictionary] = ["alg":"RS256","typ":"JWT"]
do {
let headerJWTData: Data = try? JSONSerialization.data(withJSONObject:headerJWT,options: JSONSerialization.WritingOptions.prettyPrinted)
let headerJWTBase64 = headerJWTData.base64EncodedString()
} catch headerError.customerError {
print("could not make data")
}
You can pass different options to base64EncodedString() depending on what format you need the base64 string to be in.