How do I access a nested JSON value using Alamofire and SwiftyJSON? - json

I am trying to access nested JSON results using swiftyJSON and Alamofire. My print value is nill and I believe I am not doing this correctly. What should my parameters be? I am trying to get the quote value located at http://quotes.rest/qod.json
func getAPI() {
Alamofire.request(.GET, "http://quotes.rest/qod.json", parameters: ["contents": "quotes"])
.responseJSON { response in
if let JSON = response.result.value {
print(JSON["quote"])
}
}
}

In your JSON quotes is an array so if you want to access quote of the first object you should do it by accessing first object:
func getAPI() {
Alamofire.request(.GET, "http://quotes.rest/qod.json", parameters: ["contents": "quotes"])
.responseJSON { response in
if let jsonValue = response.result.value {
let json = JSON(jsonValue)
if let quote = json["contents"]["quotes"][0]["quote"].string{
print(quote)
}
}
}
}

If the syntax of the json isn't correct, since it is fully printed anyway you should notice what's wrong.
func getAPI() {
Alamofire.request(.GET, "http://quotes.rest/qod.json", parameters: ["contents": "quotes"])
// JSON response
.responseJSON { response in switch response.result {
case .Failure(let error):
// got an error in getting the data, need to handle it
print("error calling GET, json response type :")
// print alamofire error code
let statusCode = error.code
print("error code json : \(statusCode)")
// print json response from server
if let data = response.data {
print("Response data: \(NSString(data: data, encoding: NSUTF8StringEncoding)!)")
}
// print http status code plus error string
print(NSHTTPURLResponse.localizedStringForStatusCode(statusCode))
if let httpResponse : NSHTTPURLResponse = response.response {
print("HTTP Response statusCode: \(httpResponse.statusCode)")
}
case .Success( _):
let statusCode = (response.response?.statusCode)!
print("status code json : \(statusCode)")
print("there is a response json")
//print(value)
// parse the result as JSON, since that's what the API provides and save datas as new user in coreData
guard let data = response.data else {
print("Error parsing response data")
return
}
let json = JSON(data: data)
// access first element of the array
if let postContent = json["contents"]["quotes"][0]["quote"].string{
// deal with json
}
}
}

Related

swift json serialization error : Unable to convert data to string

I'm a Korean developer, and I'm not familiar with swift.
Unfortunately the server I need to connect with is encoded in euc-kr format.
If the JSON contains Korean, it outputs the following error: Unable to convert data to string
I have verified through Json Parser that the shape of my JSON is not strange.
I am using Alamofire library for server connection.
This is my Json structure.
{"result":[{"id":"2659","member_id":"sexyback","nickname":"BJ%ED%8F%AC%EB%A1%9C%EB%A6%AC","live_YN":"N","19":"all","intro":"엄청나게 재미있는 포로리","fan":"open","acc_good":"","acc_fav":"","img_url":"%2Fpx%2Ffiles%2Fdotv_2659_f1_657.jpg"}],"total":[{"total":"6"}],"status":[{"status":"success"}]}
My logic is:
static func requestHotBJList() async -> Any {
let url = WebConstant.getHotBJList()
DLogUtil.debug(tag: #file, content: url)
do {
let data = try await HttpWrapper.requestGetTest(withUrl: url)
return JSONUtil.createJSONObject(data: data)
} catch {
return error
}
}
Get data from server via Http Wrapper.request Get. Try JSON Parsing through JSONUtil.createJSONObject(data: data) with the data of the Data type imported from here.
I get data from the server in the following way:
public static func requestGet(
withUrl url: String,
withHeader header: HTTPHeaders? = nil
) async throws -> Data {
try await withUnsafeThrowingContinuation { continuation in
AF.request(url, method: .get, headers: header).validate().responseData { response in
if let data = response.data {
continuation.resume(returning: data)
return
}
if let error = response.error {
continuation.resume(throwing: error)
return
}
fatalError("fatal error")
}
}
}
The data parsing logic is as follows :
public static func createJSONObject(data: Data) -> [String : Any] {
do {
DLogUtil.debug(tag: #file, content: "data ? \(data)")
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String : Any]
return json
}
catch let error as NSError {
DLogUtil.debug(tag: #file, content: error)
return [String : Any]()
}
}
Error is output during the parsing process. I've searched a lot, but I can't find it.
With help from #workingdogsupportUkraine, I found a way. After converting data type to NSString, NSString is encoded in utf-8 format.
var dataString = NSString(data: data, encoding: CFStringConvertEncodingToNSStringEncoding(0x0422))
var datadata = dataString!.data(using: String.Encoding.utf8.rawValue)
let json = try JSONSerialization.jsonObject(with: datadata!, options: []) as! [String : Any]
return json

Parse JSON when I get a error response in Alamofire

I've been working on API requests using Alamofire, and I want to know something for parsing the Error and to get the JSON data from the server instead of accessing the AFErrors. The code below works fine, but since I've been using .responseDecodable for decoding the response and also the MyError inherits Decodable, I was wondering if there is a similar way to decode data when I get the error response back, instead of using JSONDecoder().decode.
When I get the error response, it returns a JSON object with one value in it (message) from the server.
static let sessionManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 30
return Session(configuration: configuration)
}()
Request.sessionManager.request(endpoint, method: httpMethod, parameters: params, headers: headers)
.responseDecodable(of: resDecodeType.self) { response in
switch response.result {
case .success(let successResponse):
completed(.success(successResponse))
case .failure(let error):
let errorStatusCode = response.response?.statusCode
do {
let data = try JSONDecoder().decode(MyError.self, from: response.data!) // -> this part...
} catch {
print(error)
}
}
}
}
struct MyError: Decodable {
let message: String
}

Stringified JSON to Swift Object or JSON dictionary

In my Swift code I make a URLRequest to my node.js server:
URLSession.shared.dataTask(with: checkoutRequest, completionHandler: {
[weak self] (data: Data?, response: URLResponse?, error: Error?) in
guard let data = data,
let dataString = String(data: data, encoding: String.Encoding.utf8) else {
return
}
// Help me!!!
}).resume()
The node.js handles this request by processing a transaction through the Braintree Payments checkout API.
checkoutProcessor.processCheckout(amount, nonce, (error, result) => {
// Checkout callback
if (error) {
res.write(error.message)
res.end()
} else if (result) {
console.log(result)
res.write(JSON.stringify(result))
res.end()
}
})
As usual, if the API request fails (e.g., no signal) it returns an error but if the transaction goes through, it returns a result.
The type of the result, however, depends on whether the financial transaction fails or succeeds:
For example, the result for a successful transaction:
Object {transaction: Transaction, success: true}
result for failed transaction:
ErrorResponse {errors: ValidationErrorsCollection, params: Object, message: "Insufficient Funds", transaction: Transaction, success: false}
The dataString winds up looking like this:
{\"transaction\":{\"id\":\"m7mj3qd7\",\"status\":\"submitted_for_settlement\",\"type\":\"sale\",\"currencyIsoCode\":\"USD\",\"amount\":\"12.34\",\"merchantAccountId\":\"yourpianobar\",\"subMerchantAccountId\":null,\"masterMerchantAccountId\":null,\"orderId\":null,\"createdAt\":\"2018-09-19T03:30:27Z\",\"updatedAt\":\"2018-09-19T03:30:27Z\",\"customer\":{\"id\":\"622865439\",\"firstName\":\"Test\",\"lastName\":\"FromSwiftTest\"
which certainly resembles a JSON object but I can't seem to decode it with JSONDecoder, doing so fails. (JSONEncoder also fails)
Most solutions I see for Objectifying stringified JSON data into Swift involves writing a swift struct into which to plop all the JSON object's properties, but since this the data structure of the result is unknown on the swift end, I don't know what to do.
How do I get these objects into my swift code?
Note: I've also tried just sending res.send(result) in the node.js code but that doesn't really change anything.
This would do the trick for Swift 5:
if let data = dataString.data(using: String.Encoding.utf8) {
do {
if let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
// Use this dictionary
print(dictionary)
}
} catch _ {
// Do nothing
}
}
You can use JSONSerialization class on your data to convert it from data to Dictionary/Array based on your json response. The code can look something like below (based on my understanding) in swift 4
URLSession.shared.dataTask(with: checkoutRequest) { (data, response, error) in
guard let requestData = data, error == nil, let httpResponse = response as? HTTPURLResponse else {
// handle error
return
}
do {
if httpResponse.statusCode == 200 {
// success json
let jsonObject = try JSONSerialization.jsonObject(with: requestData, options: .allowFragments)
print(jsonObject) // jsonObject can be dictionary or Array. Typecast it based on your response
} else {
//error json
let jsonObject = try JSONSerialization.jsonObject(with: requestData, options: .allowFragments)
print(jsonObject) // jsonObject can be dictionary or Array. Typecast it based on your response
}
}
catch {
print(error.localizedDescription)
}
}.resume()

JSON_encode for ALAMOFIRE

I have a very simple Swift code to retrieve JSON data. But somehow it doesn't work properly.
Alamofire.request("*URL*").validate().responseJSON { response in
print(response.result.value)
if response.result.isSuccess {
if let userList:[[String:Any]] = response.result.value as? [[String:Any]] {
for user:[String:Any] in userList {
if let userName = user["name"] as? String {
self._myList.append(User(name: userName, value: true))
}
}
}
self.tableView.reloadData()
} else {
print(response.result.error)
}
}
During the execution, I get this error message in the console :
Optional(Alamofire.AFError.responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
The print after calling Alamofire.request is showing "nil" in the console.
What I don't understand is it's working if I use .responseString instead of .responseJSON (but it shows only a String). I really need to use .responseJSON as I need to parse the result.
My JSON that appears on the web browser is very simple as well :
[{"name":"MrX","value":"0"}]
Any idea ?
Mika.
use this
import Alamofire
import SwiftyJSON
let baseURL: String = "http://baseURL.com"
Alamofire.request(baseURL)
.responseJSON { response in
guard response.result.error == nil else {
// got an error in getting the data, need to handle it
print("error calling GET")
print(response.result.error!)
return
}
if let value = response.result.value {
let json = JSON(value) // JSON comes from SwiftyJSON
print(json) // to see the JSON response
let userName = json["name"].array // .array comes from SwiftyJSON
for items in userName! {
self._myList.append(User(name: items, value: true))
}
DispatchQueue.main.async {
self.tableView?.reloadData()
}
}
}
and take the .validate() out, if you take that out you will see more detailed error description. Hope that helps.

How to convert <AnyObject> response in AnyObject after an Alamofire request with JSON in Swift?

So I want to send a request to a specific API which is supposed to return a JSON file.
I am using Alamofire in order to get the JSON object :
dataFromAPI : JSON
Alamofire.request(.GET, myURL).responseJSON { (_, _, data) -> Void in
dataFromAPI = JSON(data)
}
My problem is that data is an array of AnyObject and the JSON function needs an AnyObject type. How can I transform one into another or resolve the problem ?
Not sure if I got your question, but will try to provide you an example of how I do it.
Changed code to your case.
Alamofire.request(.GET, myURL)
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseJSON { request, response, jsonResult in
switch jsonResult {
case .Success:
if let data = jsonResult.value as? AnyObject {
self.dataFromAPI = JSON(data)
}
case .Failure(_, let error):
print(error)
}
}
Normally I wouldn't do unwrapping to AnyObject, as it makes little sense.
I usually unwrap to [String: AnyObject] as I'm expecting Dictionary from my API, and then I attempt to convert it to my custom model class.
Correct me if I miss-understood the question. :)
Alamofire returns a Result<AnyObject> object. You should check if the result is a success or a failure before extracting the JSON:
Alamofire.request(.GET, myURL)
.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)!)")
}
}
}