Strange JSON API response SWIFT - ios

I am sending an HTTP POST request to an API in swift and it is supposed to respond with:
{
"results": [
{
"alternatives": [
{
"transcript": "how old is the Brooklyn Bridge",
"confidence": 0.98267895
}
]
}
]
}
However, I am receiving this through the print(jsonResponse) function:
Optional({
results = (
{
alternatives = (
{
confidence = "0.9688848";
transcript = "how old is the Brooklyn Bridge";
}
);
}
);
})
Is there any reason why the response is not arriving in the correct format as indicated in the API documentation? I need to Decode the response to obtain the "transcript" value. However, I am receiving the following error:
keyNotFound(CodingKeys(stringValue: "transcript", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"transcript\", intValue: nil) (\"transcript\").", underlyingError: nil))
Maybe my request isn't optimal... Here's my code, any help is appreciated!
let parameters = ["config": ["encoding": "FLAC", "sampleRateHertz": "16000", "languageCode": "en-AU"], "audio": ["uri":"gs://cloud-samples-tests/speech/brooklyn.flac"]]
guard let url = URL(string: "https://speech.googleapis.com/v1/speech:recognize?key=AIzaSyDqYpPQIabwF5L-WibBxtVsWRBrc8uKi4w") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: []) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error.localizedDescription)
}
URLSession.shared.dataTask(with: request) { (data , response , error) in
guard let data = data else {return}
do {
let jsonResponse = (try? JSONSerialization.jsonObject(with: data, options: []))
print("\(jsonResponse)")
let course = try JSONDecoder().decode(Course.self , from : data)
print(course.transcript)
} catch {
print(error)
}
}.resume()
Here is my Course code block: Do I need to include the other components in the struct as well as the transcript?
struct Course: Decodable {
let transcript: String
enum CodingKeys: String, CodingKey {
case transcript = "transcript"
}
}

jsonResponse is an Optional Dictionary, and thus that's why it's debug description looks like what you printed rather than pure JSON as you were looking for. Your problem likely is that your Decodeable objects are not properly setup - as by the looks of it you only have one Course. You'll likely need two more Response which contains a list of Alternatives. And then in Alternative you have a list of Courses.
Structure your objects like this, and it should do the trick:
struct Response: Decodable {
let results: [Alternative]
}
struct Alternative: Decodable {
let alternatives: [Course]
}
struct Course: Decodable {
let transcript: String
let confidence: Float
}
And then swap this line:
let course = try JSONDecoder().decode(Course.self , from : data)
With this change:
let course = try JSONDecoder().decode(Response.self, from: data).results[0].alternatives[0]

Related

Decode nested JSON arrays and dictionaries in Swift using Structs with JSONserialization

I am trying to create some structs to decode some JSON received from an API using JSONSerialization.jsonObject(with: data, options: [])
This is what the JSON looks like:
{"books":[{"title":"The Fountainhead.","author":"Ayn Ranyd"},{"title":"Tom Sawyer","author":"Mark Twain"},{"title":"Warhol","author":"Blake Gopnik"}]}
Here are the structs that I am trying to use for decoding.
struct BooksReturned : Codable {
let books : [Book]?
}
struct Book : Codable {
let BookParts: Array<Any>?
}
struct BookParts : Codable {
let titleDict : Dictionary<String>?
let authorDict : Dictionary<String>?
}
The error is:
The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
The non-working code I am using to decode is:
let task = session.dataTask(with: url) { data, response, error in
if let data = data, error == nil {
let nsdata = NSData(data: data)
DispatchQueue.main.async {
if let str = String(data: data, encoding: .utf8) {
let json = try? JSONSerialization.jsonObject(with: data, options: [])
do {
let mybooks = try JSONDecoder().decode(BooksReturned.self, from: data)
//do something with book
}
} catch {
print(error.localizedDescription)
print(error)
}
}
}
} else {
// Failure
}
}
task.resume()
}
I have some very limited ability to change JSON. The only thing I can do is remove the "books" : Everything else is received from an external API.
Thanks for any suggestions on how to get this to work.
The JSON you provided seems to be valid. Modify your Book model and the decoding part as the following.
Model:
struct Book: Codable {
let title, author: String
}
Decoding:
let task = session.dataTask(with: url) { data, response, error in
if let data = data, error == nil {
DispatchQueue.main.async {
do {
let mybooks = try JSONDecoder().decode(BooksReturned.self, from: data)
print(mybooks)
}
} catch {
print(error.localizedDescription)
print(error)
}
}
} else {
// Failure
}
task.resume()

My json response is coming in different format than I expected

In browser my url gives results in perfect JSON format as follows
"articles": [
{
"source": {
"id": "the-times-of-india",
"name": "The Times of India"
},
"author": "Times Of India",
But Where as in Xcode output the response I am getting is as follows. How to convert this response into perfect json format
{
articles = (
{
author = "Times Of India";
content = "Hyderabad: Senior Police officials arrive at the site of the encounter. All four accused in the rape
description = "India News: All four accused in the rape and murder of woman veterinarian in Telangana have been killed in an encounter with the police. Cops claimed they tried t";
publishedAt = "2019-12-06T04:15:00Z";
source = {
name = "The Times of India";
};
},
I am using the following code to decode the json data
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do{
//here dataResponse received from a network request
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: [])
print(jsonResponse) //Response result
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
Please help me with this issue.
first you must create a Decodable struct and put it before viewController class:
struct YourArrray: Decodable {
let author: String?
let content: String?
let location: String?
let description : String?
let publishedAt : String?
let name: String?
}
declare your Url:
let jsonUrlString = "https://yourUrljson"
after that create your struct array var:
var myVar = [YourArrray]()
now you can procede to decode json:
fileprivate func fetchJsonObject() {
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, respons, err) in
guard let data = data else { return }
do {
let jsonResponse = try JSONDecoder().decode([myVar].self, from: data)
print(jsonResponse)
} catch let jsonErr {
print("Error serializing:", jsonErr)
}
}.resume()
}
Now you can simply call the function fetchJsonObject() and you're done
Hope this help :)

Swift 4 Decodable parsing json data in array

I have a problem with parsing data from web service, it seems that the decodable protocol couldn't parse this json
This is my parsing data using generics.
public func requestGenericData<T: Decodable>(urlString: String, httpMethod: String?, token: String!, completion: #escaping(T) ->()) {
let fullStringUrl = url + urlString
guard let url = URL(string: fullStringUrl) else { return }
guard let token = token else { return }
var urlRequest = URLRequest(url: url)
urlRequest.setValue("application/json", forHTTPHeaderField: "accept")
urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
urlRequest.httpMethod = httpMethod
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if self.isInternetAvailable() {
guard let data = data else { return }
if let httpResponse = response as? HTTPURLResponse {
if httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 {
do {
let obj = try JSONDecoder().decode(T.self, from: data)
completion(obj)
} catch {
print("Error: \(String(describing: error))\n StatusCode: \(httpResponse.statusCode)")
}
}
}
} else {
showAlert(title: "No Internet Connect", message: "Please open your network and try again.", alertStyle: .alert, buttonTitle: "OK", buttonStyle: .default)
return
}
}.resume()
}
This is my model
struct JobWithCategory: Decodable {
let jobTypeID: Int
let jobCategoryID: Int
let name: String
let getJobs: [getJobs]
}
struct getJobs: Decodable {
let name: String
let description: String
}
struct JobCategories: Decodable {
let jobCategories: [JobWithCategory]
}
apiHelper.requestGenericData(urlString: "url/on/something/else", httpMethod: "GET", token: token) { (jobCategories: [JobCategories]) in
print(jobCategories)
}
Now i'm having with this issue on my console printed:
Error: typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
What do I missed or did something wrong with my implementation? Could someone help me out on this one, and so please elaborate why is this happening so I can have a good grasp on whats going on on my code.
Thanks in advance :)
Please read the error message
Expected to decode Array but found a dictionary instead
The expected side shows what you are doing (wrong) and the found side shows the actual type.
In terms of Decodable a dictionary is a struct. So it's
...{ (jobCategories: JobCategories) in
Because you use
[JobCategories]
as the completion T is inferred to array so
T.self = [JobCategories].self
not a dictionary , so try this
apiHelper.requestGenericData(urlString: "url/on/something/else",
httpMethod: "GET", token: token) { (jobCategories:JobCategories) in
print(jobCategories.jobCategories)
}

How to display parsed JSON data in Swift?

After parsing JSON trough an API call i've had trouble converting the parsed JSON into the data that i want to display in my ViewController. I am able to print the JSON data in the console but i am not able to only get the value of "word: .." from the data i want to display. This is an example of a JSON object:
{
"metadata": {},
"results": [
{
"id": "eurhythmic",
"word": "eurhythmic"
}
]
}
The struct i'm using is the following:
// Struct RandomWords
struct StoreRandomWords: Codable {
let results: [Result]
}
struct Result: Codable {
let word: String
}
The code to fetch the data from the API:
func fetchRandomWord(completion: #escaping (StoreRandomWords?) -> Void) {
let language = "en"
let filters = "registers%3DRare%3Bdomains%3DArt?"
let limit = "limit=1"
let url = URL(string: "https://od-api.oxforddictionaries.com:443/api/v1/wordlist/\(language)/\(filters)\(limit)")!
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue(appId, forHTTPHeaderField: "app_id")
request.addValue(appKey, forHTTPHeaderField: "app_key")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
do {
let jsonData = try JSONDecoder().decode(StoreRandomWords.self, from: data)
print(jsonData)
} catch {
print(error)
}
} else {
print(error)
print(NSString.init(data: data!, encoding: String.Encoding.utf8.rawValue)!)
}
}
task.resume()
}
And the code from ViewController in which i want to load the "word":
// Do any additional setup after loading the view.
override func viewDidLoad() {
super.viewDidLoad()
// Fetch data for Daily Word.
ContextController.shared.fetchRandomWord( completion: { (storeRandomWord) in
if let storeRandomWord = storeRandomWord {
//
}
})
}
Which gives me this output in the console:
StoreRandomWords(results: [GetContext.Result(word: "eurhythmic")])
And my goal is to literally display the value word which is here "eurhythmic".
(I'm a beginner and not a native speaker of English so any feedback on how to improve the code or the question would be helpful and welcome to. )
You will have to loop the array or access the index science storeRandomWords.results is an array.
For example print the first result:
if let storeRandomWord = storeRandomWord {
print(storeRandomWord.results.first.word)
}

not able to parse the JSON using JSON decoder

I am new to swift and i am trying to integrate the CCAvenue payment gateway. I am hitting the server to get the payment option list from the CCAvenue server which i an getting in the response but i am not able to parse the data into a JSON object, it is throwing some exception. Thanks in advance for the help
here is my code
override func viewDidLoad() {
super.viewDidLoad()
let urlAsString = "https://test.ccavenue.com/transaction/transaction.do?"
let myRequestString = "command=\(COMMAND)&currency=\(currency)&amount=\(amount)&access_code=\(accessCode)&customer_identifier=\(customerIdentifier)"
let myRequestData = NSData.init(bytes: myRequestString.cString(using: .utf8), length: myRequestString.count) as Data
let request = NSMutableURLRequest.init(url: URL(string: urlAsString)!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
request.setValue(urlAsString, forHTTPHeaderField: "Referer")
request.setValue("Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", forHTTPHeaderField: "User-Agent")
request.httpMethod = "POST"
request.httpBody = myRequestData
let requestData = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
let responseData = NSString.init(data: data!, encoding: String.Encoding.ascii.rawValue)
if error == nil {
print("\(responseData)")
}
// if request is error free then decode the json using json decoder and assigning the values to the array
guard let data = data else {return}
do{
let a = try JSONDecoder().decode([CCPaymentOption].self, from: data)
print("\(String(describing: a))")
}catch {
print("Error")
}
}.resume()
print("\(requestData)")
}
this is what i am getting in my debugger
Optional({"payOptions":[{"payOpt":"OPTCRDC","payOptDesc":"Credit Card","cardsList":"[{\"cardName\":\"Diners Club\",\"cardType\":\"CRDC\",\"payOptType\":\"OPTCRDC\",\"payOptDesc\":\"Credit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\"},{\"cardName\":\"MasterCard\",\"cardType\":\"CRDC\",\"payOptType\":\"OPTCRDC\",\"payOptDesc\":\"Credit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\",\"statusMessage\":\"\"},{\"cardName\":\"Visa\",\"cardType\":\"CRDC\",\"payOptType\":\"OPTCRDC\",\"payOptDesc\":\"Credit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\"}]"},{"payOpt":"OPTDBCRD","payOptDesc":"Debit Card","cardsList":"[{\"cardName\":\"MasterCard Debit Card\",\"cardType\":\"DBCRD\",\"payOptType\":\"OPTDBCRD\",\"payOptDesc\":\"Debit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\"},{\"cardName\":\"Visa Debit Card\",\"cardType\":\"DBCRD\",\"payOptType\":\"OPTDBCRD\",\"payOptDesc\":\"Debit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\"}]"},{"payOpt":"OPTNBK","payOptDesc":"Net Banking","cardsList":"[{\"cardName\":\"AvenuesTest\",\"cardType\":\"NBK\",\"payOptType\":\"OPTNBK\",\"payOptDesc\":\"Net Banking\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\",\"statusMessage\":\"\"}]"}]})
Error
There are many points needed to be checked while decoding the object.
Make sure your CCPaymentOption Model addopt the Protocol Codable.
While Decoding the Data , make sure you are aware of thing that your response is in form of Dictionary or Array
lets say you are getting the Array in response , in that case you can directly use [CCPaymentOption] in JSONDecoder().decode() method.
And if you are getting the Dictionary from the server response then you need to decode the object on that way.
Example of CCPaymentOption Model for point no 3.
struct CCPaymentOption : Codable {
var amount:String // Note down that , please use exact same
// key as you are getting from server side.
}
do {
let arrPaymentOptions = try JSONDecoder().decode([CCPaymentOption].self, from: responseData)
print(arrPaymentOptions)
///... Array of Your Model reference.
} catch {
print(error)
}
Example of CCPaymentOption Model for point no 4.
struct CCPaymentOption : Codable {
var amount:String
}
struct responseDictionary : Codable {
var paymentOption:[CCPaymentOption] // Note down that , please
// use exact same key as you
// are getting from server
// side.
}
do {
let responseDict = try JSONDecoder().decode(responseDictionary.self, from: responseData)
print(responseDict.paymentOption)
// responseDict.paymentOption is the Array of Your
// Model reference.
} catch {
print(error)
}
Please try out the below one if you don't want to use the JSON decoder. :-
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard error == nil else { return }
guard let responseData = data else { return }
do {
if let jsonObject = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String:Any] {
///... All you want is here jsonObject is the Dictionary (required , not an optional)
}
} catch {
print(error)
}
}).resume()
After getting the data from server side , you need to do JSONSerialization with the help of native method. This method will return the Dictionary or Array (Depending on your server response.)

Resources