How to parse this json with alamofire - ios

How to parse this json in my code? (Как распарсить этот json в моем коде?). What data model to collect? (какую модель данных собирать?). I don’t understand how to cast dictionaries in dictionaries later. (Не пойму как потом кастить словари в словарях).
I get an error opposite let artist:
Value of type 'Dictionary.Element' (aka '(key: String, value: Dictionary)') has no subscripts
func fetchCurrentChartsWithAlamofire(apiMethod: String) {
let url = "https://"
request(url).validate().responseJSON { responseData in
switch responseData.result {
case .success(let value):
guard let jsonData = value as? [String:[String:AnyObject]] else { return }
for artists in jsonData {
let artist = Artist(name: artists["artists"])
}
case .failure(let error):
print(error)
}
}
}
Here is json in the browser:
{
"artists": {
"artist": [
{
"name": "The Weeknd",
}
]
}
}

Here is how you can parse this
struct Artist:Decodable {
let artists:Artists
}
struct Artists:Decodable {
let artist: [ArtistName]
}
struct ArtistName:Decodable {
let name: String
}
For json

Related

How to print error message while parsing in swift

this is json error structure:
{
"jsonrpc": "2.0",
"error": {
"code": "-32700",
"message": "Parse error",
"meaning": "Could not decode token: Error while decoding to JSON: Syntax error"
}
}
this is success response structure:
{
"jsonrpc": "2.0",
"result": {
"users": [
{
"id": 371,..... so on data
here am unable to print error > message in my code
code: here able to print json response result or error dictionary in case .success(_) print statement but how to print only error > meaning
how to print error's "Parse error" in below code, please guide me
class GeneralResponse<T: Codable>: Codable {
var result: T?
let error: ErrorClass?
}
struct ErrorClass: Codable {
let code, message, meaning: String?
}
struct RequestObject {
var params: [String: AnyObject]? = nil
var method: HTTPMethod
var path: String
var isTokenNeed: Bool = false
var vc: UIViewController?
}
class NetworkManager {
private let decoder: JSONDecoder
static let sharedInstance = NetworkManager()
public init(_ decoder: JSONDecoder = JSONDecoder()) {
self.decoder = decoder
}
public func serviceCall<T: Codable>(_ objectType: T.Type,
with request: RequestObject,
completion: #escaping (T?, Error?) -> Void) {
let paramsDict = ["jsonrpc" : "2.0", "params" : request.params ?? nil] as [String : Any?]
AF.request(request.path, method: request.method, parameters: paramsDict as [String : AnyObject], encoding: JSONEncoding.default, headers: "Accept": "application/json")
.responseJSON { response in
switch response.result {
case .success(_):
do {
print("only json respopnse \(response)")
let data = response.data
let responseData = try self.decoder.decode(T.self, from: data ?? Data())
completion(responseData, nil)
} catch {
completion(nil, error)
print("in catch \(error)")
}
case .failure(let AFError):
let error = AFError
print(error.localizedDescription)
print("failure error: \(error)")
}
}
}
}
o/p for error in above call with print("only json respopnse \(response)")
only json respopnse success({
error = {
code = "-32700";
meaning = "Token Signature could not be verified.";
message = "Parse error";
};
jsonrpc = "2.0";
})
EDIT 2: here success response not coming in viewcontroller
struct PostModel: Codable {
let jsonrpc: String?
let result: PostResult?
}
struct PostResult: Codable {
let users: [PostUser]?
}
struct PostUser: Codable {
let id: Int?
}
and calling like this in vc
var k12Data: PostModel?
let paramet = ["location": "", "country": "", "gender": "", "keyword": ""] as [String : AnyObject]
let request = RequestObject(params: paramet, method: .post, path: "https://phpwebdevelopmentserv/", isTokenNeed: true, vc: self)
NetworkManager.sharedInstance.serviceCall(PostModel.self, with: request) { (response, error) in
print("viewcontroller data \(response)")
}
o/p
viewcontroller data Optional(TestigAllInOne.PostModel(jsonrpc: nil, result: nil))
You didn´t show us your Postmodel so I am asuming it is of some type of GeneralResponse<T> class.
You should decode to your GeneralResponse class:
let responseData = try self.decoder.decode(GeneralResponse<T>.self, from: data)
now you can print your error if one exists:
print(responseData.error?.meaning ?? "no error")
and call your completion handler like this:
guard let result = responseData.result else{
// unknown error has occured
completion(nil, nil) // you probably need to create a custom error here
return
}
completion(result, nil)
the main essence is to not pass GeneralResponse as argument to your function but the result type you are expecting. So if for example your User type looks like this:
struct User: Codable{
var id: String
}
call it like this:
networkmanager.serviceCall([User].self, with: RequestObject(...)) { users, error in
// users is of type `[User]` here.
}

swift decode json data

I have following json data returns form php.
{"Response":"OK","Data":[{"id":"1","organization_name":"Organization","description":"Description","address":"Address1, Ny, USA"}]}
In need to decode it using swift
Below is my code.
struct OrgData: Decodable {
let data: [Data]
enum CodingKeys : String, CodingKey {
case data = "Data"
}
}
struct Data: Decodable {
let id: String
let address: String
let description: String
let organization_name: String
}
and I am decoding it using
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
guard let dataObj = try? JSONDecoder().decode(OrgData.self, from: data) else {
print("Error: Couldn't decode data ")
return
}
..................
But no data I am getting in dataObj.
I am referring this article
https://roadfiresoftware.com/2018/02/how-to-parse-json-with-swift-4/
Create a function in a helper class
func decodedObject<T: Decodable>(_ type: T.Type, dictionaryData: JSONDictionary) throws -> T? {
guard let jsonData = try? JSONSerialization.data(withJSONObject: dictionaryData,
options: JSONSerialization.WritingOptions.prettyPrinted) else {
return nil
}
let decodedData = try self.decode(type, from: jsonData)
return decodedData
}
Now call it like follows:
guard let dataObj = try? JSONDecoder().decodedObject(OrgData.self, dictionaryData: data) else {
print("Error: Couldn't decode data ")
return
}
There were some issues with how your structs were configured:
There is no field for the "Response" value
Data is an array, not a String
The following appears to properly output the json you give:
import Foundation
let json =
"""
{"Response":"OK","Data":[{"id":"1","organization_name":"Organization","description":"Description","address":"Address1, Ny, USA"}]}
"""
// MARK: - OrgData
struct OrgData: Codable {
let response: String
let data: [Datum]
enum CodingKeys: String, CodingKey {
case response = "Response"
case data = "Data"
}
}
// MARK: - Datum
struct Datum: Codable {
let id, organizationName, datumDescription, address: String
enum CodingKeys: String, CodingKey {
case id
case organizationName = "organization_name"
case datumDescription = "description"
case address
}
}
guard let data = json.data(using: .utf8) else {
return
}
do {
let dataObj = try JSONDecoder().decode(OrgData.self,
from: data)
print(dataObj)
// Optional(__lldb_expr_1.OrgData(response: "OK", data: [__lldb_expr_1.Datum(id: "1", organizationName: "Organization", datumDescription: "Description", address: "Address1, Ny, USA")]))
} catch {
print(error)
}

Unstable error message from the API in Xcode?

while i am trying to decode some json data using Alamofire and PromiseKit, I am encountering some unstable error message.. why I am saying " unstable error message is that sometimes when I hit the API .GET request, i am getting the response in full format, without any error, but after a couple of times running the application Xcode throwing an Alamofire Response Serialization Error which is so confusing.. . I have implemented Coding keys too, data and json response formats are also in right format.. can anyone please help me decode this error message .?
here is the response i got from the API in one of random application run:
and after the next run, here is the error message from the xcode console:
Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))))
if any part of the code is required for more analysis, please let me know so that I will update the question contents with those required pieces of code.. .
here is my json response model structs:
// MARK: - TickerByPair
struct TickerByPair {
var price, ask: Double
var askVolume: Double
var bid: Double
var bidVolume, volume: Double
var time: String
}
extension TickerByPair: Decodable{
enum TrackCodingKeys: String, CodingKey {
case price = "price"
case ask = "ask"
case askVolume = "askVolume"
case bid = "bid"
case bidVolume = "bidVolume"
case volume = "volume"
case time = "time"
}
init(from decoder: Decoder) throws {
let trackContainer = try decoder.container(keyedBy: TrackCodingKeys.self)
if trackContainer.contains(.price){
price = try trackContainer.decode(Double.self, forKey: .price)
}else{
price = 0
}
if trackContainer.contains(.ask) {
ask = try trackContainer.decode(Double.self, forKey: .ask)
} else {
ask = 0
}
if trackContainer.contains(.askVolume) {
askVolume = try trackContainer.decode(Double.self, forKey: .askVolume)
} else {
askVolume = 0
}
if trackContainer.contains(.bid) {
bid = try trackContainer.decode(Double.self, forKey: .bid)
} else {
bid = 0
}
if trackContainer.contains(.bidVolume) {
bidVolume = try trackContainer.decode(Double.self, forKey: .bidVolume)
} else {
bidVolume = 0
}
if trackContainer.contains(.volume) {
volume = try trackContainer.decode(Double.self, forKey: .volume)
} else {
volume = 0
}
if trackContainer.contains(.time) {
time = try trackContainer.decode(String.self, forKey: .time)
}
else {
time = ""
}
}
}
// MARK: - TradingPairElement
struct TradingPair {
var id: Int
var name: String
var quoteAsset: String
var baseAsset: String
}
extension TradingPair: Decodable {
enum TrackCodingKeys: String, CodingKey {
case id = "id"
case name = "name"
case quoteAsset = "quoteAsset"
case baseAsset = "baseAsset"
}
init(from decoder: Decoder) throws {
let trackContainer = try decoder.container(keyedBy: TrackCodingKeys.self)
if trackContainer.contains(.id){
id = try trackContainer.decode(Int.self, forKey: .id)
}else{
id = 0
}
if trackContainer.contains(.name) {
name = try trackContainer.decode(String.self, forKey: .name)
} else {
name = ""
}
if trackContainer.contains(.quoteAsset) {
quoteAsset = try trackContainer.decode(String.self, forKey: .quoteAsset)
} else {
quoteAsset = ""
}
if trackContainer.contains(.baseAsset) {
baseAsset = try trackContainer.decode(String.self, forKey: .baseAsset)
} else {
baseAsset = ""
}
}
}
and the API .GET request using Alamofire :
class ServerCommunicator {
static func getAssets() -> Promise<[Assets]> {
let decoder = JSONDecoder()
return Promise { seal in
AF.request(API.assets, method: .get, parameters: .none, headers: .none).responseDecodable(of: [Assets].self, decoder: decoder) { response in
switch response.result {
case .success(let assets):
return seal.fulfill(assets)
case .failure(let error):
return seal.reject(error)
}
}
}
}
static func getPairs() -> Promise<[TradingPair]> {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
return Promise { seal in
AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseDecodable(of: [TradingPair].self, decoder: decoder) { response in
switch response.result {
case .success(let pairs):
return seal.fulfill(pairs)
case .failure(let error):
return seal.reject(error)
}
}
}
}
static func getPair(with pairName: String?) -> Promise<TickerByPair> {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
return Promise { seal in
AF.request(API.getPairByTicker(pairName: pairName!), method: .get, parameters: .none, headers: .none).responseDecodable(of: TickerByPair.self, decoder: decoder) { response in
switch response.result {
case .success(let ticker):
return seal.fulfill(ticker)
case .failure(let error):
return seal.reject(error)
}
}
}
}
}
and my json response format is also as follows:
[
{
"name": "ETH-KRW",
"baseAsset": "ETH",
"quoteAsset": "KRW"
}, {
"name": "BTC-KRW",
"baseAsset": "BTC",
"quoteAsset": "KRW"
}, {
"name": "BCH-KRW",
"baseAsset": "BCH",
"quoteAsset": "KRW"
}
]
-------------
{
"price": 10194500,
"ask": 10195000,
"bid": 10184500,
"volume": 1752.05558316,
"time": "2018-03-14T03:50:41.184Z"
}
If there are cases where your API that supposed to return array, returns dictionary instead, you can try re-implementing the convenient responseDecodable(of:decoder:) function of Alamofire.
Use the responseData(queue:completionHandler:) and try to decode your response data to array and if that fails try again to an object type.
Your getPairs() function for example could be something like this:
static func getPairs() -> Promise<[TradingPair]> {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
return Promise { seal in
AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseData { response in
switch response.result {
case .success(let data):
do {
let pairs = try decoder.decode([TradingPair].self, from: data)
return seal.fulfill(pairs)
} catch DecodingError.typeMismatch {
do {
let pair = try decoder.decode(TradingPair.self, from: data)
return seal.fulfill([pair])
} catch {
return seal.reject(error)
}
} catch {
return seal.reject(error)
}
case .failure(let error):
return seal.reject(error)
}
}
}
}
Or more generally, make an extension of DataRequest like this:
extension DataRequest {
#discardableResult func responseArray<T: Decodable>(
of type: T.Type,
decoder: DataDecoder,
completionHandler: #escaping (AFDataResponse<[T]>) -> Void
) -> Self {
responseData { response in
switch response.result {
case .success(let data):
do {
let array = try decoder.decode([T].self, from: data)
let dataResponse = self.dataResponse(from: response, result: .success(array))
completionHandler(dataResponse)
} catch DecodingError.typeMismatch {
do {
let object = try decoder.decode(T.self, from: data)
let dataResponse = self.dataResponse(from: response, result: .success([object]))
completionHandler(dataResponse)
} catch {
let dataResponse: AFDataResponse<[T]> = self.dataResponse(
from: response,
result: .failure(.responseSerializationFailed(reason: .decodingFailed(error: error)))
)
completionHandler(dataResponse)
}
} catch {
let dataResponse: AFDataResponse<[T]> = self.dataResponse(
from: response,
result: .failure(.responseSerializationFailed(reason: .decodingFailed(error: error)))
)
completionHandler(dataResponse)
}
case .failure(let error):
let dataResponse: AFDataResponse<[T]> = self.dataResponse(from: response, result: .failure(error))
completionHandler(dataResponse)
}
}
}
private func dataResponse<T: Decodable>(from response: AFDataResponse<Data>, result: Result<[T], AFError>) -> AFDataResponse<[T]> {
return .init(
request: response.request,
response: response.response,
data: response.data,
metrics: response.metrics,
serializationDuration: response.serializationDuration,
result: result
)
}
}
and then your getPairs() function will be much simpler:
static func getPairs() -> Promise<[TradingPair]> {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
return Promise { seal in
AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseArray(of: TradingPair.self, decoder: decoder) { response in
switch response.result {
case .success(let pairs):
return seal.fulfill(pairs)
case .failure(let error):
return seal.reject(error)
}
}
}
}

how to get value from json array in swift4

I can't getting json value into variable.I'm printing value but the problem is I can't getting json value without array
here is my json
{
"Categories": [
"city",
"delhi"
]
}
I want to categories value with array im printing value with array
here is my code
do{
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: AnyObject]
print(json as AnyObject)
if let Categories = json["Categories"] {
print(Categories)
}
You need
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! [String:[String]]
let arr1 = json["Categories"]!
let str1 = arr1.joined(separator: ":")
print(str1)
// or
let decoded = try JSONDecoder().decode(Root.self, from: data)
let str = decoded.categories.joined(separator: ":")
print(str)
} catch {
print(error)
}
or use
struct Root: Codable {
let categories: [String]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
}
}
Make your life easier with Codable. First create custom model for your response
struct Response: Decodable {
let categories: [String]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
}
}
Then decode your data which your receive using JSONDecoder
if let data = data {
do {
let decoded = try JSONDecoder().decode(Response.self, from: data)
let string = decoded.categories.joined(separator: ", ") // if u need to join
// your array to
// single `String`
print(string)
} catch { print(error) }
}
Use built in swift support for decoding json by conforming to Decodable and also conform to CustomStringConvertible to get a string representation of the values
struct Item: Decodable, CustomStringConvertible {
let categories: [String]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
}
public var description: String {
return categories.joined(separator: " ")
}
}
let decoder = JSONDecoder()
do {
let result = try decoder.decode(Item.self, from: data)
let descr = result.description
print(descr)
} catch {
print(error)
}
//Model Class should be like this
struct JsonResposne : Codable {
let categories : [String]?
enum CodingKeys: String, CodingKey {
case categories = "Categories"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
categories = try values.decodeIfPresent([String].self, forKey: .categories)
}
}
func getCategoriesResponse() {
Alamofire.request(requestUrl, method: .post, parameters: params, encoding: URLEncoding.default).responseJSON { (response) in
switch response.result {
case .success:
if response.data != nil {
do {
let decoder = JSONDecoder()
let apiResponse:JsonResponse = try decoder.decode(JsonResponse.self, from: responseData)
print(apiResponse.categories.count)
}catch {
print(error.localizedDescription)
}
}
}
break
case .failure:
print("There was something with your call")
break
}
}
}

Serialize JSON string that contains escaped (backslash and double quote) Swift return Badly formed object

I have response string from the backend like this:
{
"status": "success",
"data": "{\"name\":\"asd\",\"address\":\"Street 1st\"}"
}
I think the problem was because the double quote (") in the data String. When I remove the double quote, the serialization was success. But the response is from backend and I have to deal with it.
Anyone can solve this problem?
Thank you.
Here is the playground code.
import Foundation
var jsonStr = """
{
"status": "success",
"data": "{\"name\":\"asd\",\"address\":\"Street 1st\"}"
}
"""
let data = jsonStr.data(using: .utf8)
if let d = data {
do {
let o = try JSONSerialization.jsonObject(with: d)
print(o)
} catch let e {
print(e)
}
} else {
print("DATA conversion ERROR")
}
First of all if you wrap the JSON in the literal string syntax of Swift 4 you have to escape the backslashes.
let jsonStr = """
{
"status": "success",
"data": "{\\"name\\":\\"asd\\",\\"address\\":\\"Street 1st\\"}"
}
"""
You got nested JSON. The value for key data is another JSON string which must be deserialized separately
let jsonData = Data(jsonStr.utf8)
do {
if let object = try JSONSerialization.jsonObject(with: jsonData) as? [String:String] {
print(object)
if let dataString = object["data"] as? String {
let dataStringData = Data(dataString.utf8)
let dataObject = try JSONSerialization.jsonObject(with: dataStringData) as? [String:String]
print(dataObject)
}
}
} catch {
print(error)
}
Or – with a bit more effort but – much more comfortable with the (De)Codable protocol
struct Response : Decodable {
private enum CodingKeys : String, CodingKey { case status, data }
let status : String
let person : Person
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
status = try container.decode(String.self, forKey: .status)
let dataString = try container.decode(String.self, forKey: .data)
person = try JSONDecoder().decode(Person.self, from: Data(dataString.utf8))
}
}
struct Person : Decodable {
let name, address : String
}
let jsonStr = """
{
"status": "success",
"data": "{\\"name\\":\\"asd\\",\\"address\\":\\"Street 1st\\"}"
}
"""
let jsonData = Data(jsonStr.utf8)
do {
let result = try JSONDecoder().decode(Response.self, from: jsonData)
print(result)
} catch {
print(error)
}
You have an error in your code ,as you write it in code like that """json""",
also
data is String not dictionary , so you will need to convert to data then JSONSerialization again
check code i add your response in Json file and parse it , and work correctly
So just write this in json file called it data.json
{
"status": "success",
"data": "{\"name\":\"asd\",\"address\":\"Street 1st\"}"
}
and use this :
guard let jsonFile = Bundle.main.path(forResource: "data", ofType: "json") else { return}
guard let data = try? Data(contentsOf: URL(fileURLWithPath: jsonFile), options: .mappedIfSafe) else {return}
if let response = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves) {
print(response)
if let dataInDictionary = response as? [String:Any] , let addresData = dataInDictionary["data"] as? String {
if let jsonData = addresData.data(using: .utf8),
let dictionary = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableLeaves) as? [String:Any]{
print(dictionary)
}
}
}
Here is another example based on the answer #vadian
Swift 4 - Using Codable
This was the json that I received:
{
"error_code": 0,
"result": {
"responseData": "{\"emeter\":{\"get_realtime\":{\"voltage_mv\":237846,\"current_ma\":81,\"power_mw\":7428,\"total_wh\":1920,\"err_code\":0}}}"
}
}
The JSON part with backslashes is equal to this:
{
"emeter": {
"get_realtime": {
"voltage_mv": 237846,
"current_ma": 81,
"power_mw": 7428,
"total_wh":19201,
"err_code":0
}
}
}
And this was the code that I used:
import Foundation
class RealtimeEnergy: Codable {
let errorCode: Int
let result: ResultRealtimeEnergy?
let msg: String?
enum CodingKeys: String, CodingKey {
case errorCode = "error_code"
case result, msg
}
init(errorCode: Int, result: ResultRealtimeEnergy?, msg: String?) {
self.errorCode = errorCode
self.result = result
self.msg = msg
}
}
class ResultRealtimeEnergy: Codable {
let responseData: String
var emeter: Emeter
enum CodingKeys: String, CodingKey {
case responseData
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
responseData = try container.decode(String.self, forKey: .responseData)
let dataString = try container.decode(String.self, forKey: .responseData)
emeter = try JSONDecoder().decode(Emeter.self, from: Data(dataString.utf8))
}
}
class Emeter: Codable {
let emeter: EmeterClass
init(emeter: EmeterClass) {
self.emeter = emeter
}
}
class EmeterClass: Codable {
let getRealtime: GetRealtime
enum CodingKeys: String, CodingKey {
case getRealtime = "get_realtime"
}
init(getRealtime: GetRealtime) {
self.getRealtime = getRealtime
}
}
class GetRealtime: Codable {
let voltageMv, currentMa, powerMw, totalWh: Int
let errCode: Int
enum CodingKeys: String, CodingKey {
case voltageMv = "voltage_mv"
case currentMa = "current_ma"
case powerMw = "power_mw"
case totalWh = "total_wh"
case errCode = "err_code"
}
init(voltageMv: Int, currentMa: Int, powerMw: Int, totalWh: Int, errCode: Int) {
self.voltageMv = voltageMv
self.currentMa = currentMa
self.powerMw = powerMw
self.totalWh = totalWh
self.errCode = errCode
}
}

Resources