JSONDecoder can't decode Array but can decode String - ios

I have a struct that I would like to parse from Json:
struct Subsidiary: Decodable {
let id: Int?
let subsidiary_ref: String?
let name: String?
let address: String?
}
I try to parse it like:
let sub: [Subsidiary] = try! JSONDecoder().decode([Subsidiary].self, from: data)
where data is Data type from
session.dataTask(with: urlRequest) { (data, response, error) in
and it gives me an error
Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a string/data instead.", underlyingError: nil))
If I do it like this:
let sub: String = try! JSONDecoder().decode(String.self, from: data)
it works and it gives me son
[
{"id": 5913, "subsidiary_ref": "0000159", "name": "Mercator Hipermarket Koper", "address": "Poslov.98-Koper,Dolinska C", "city": "Koper", "coordinates_x": null, "coordinates_y": null, "phone_number": "+386 56-636-800", "frequency": "A", "channel": "Food", "subchannel": "Hypermarket", "customer": 7, "day_planned": true, "badge_visible": true, "open_call": true, "number_of_planned": 13, "number_of_calls": 22, "last_call_day": 3, "notes": " notesi ki ne smejo nikoli zginiti bla marko bdsa"},
{"id": 5870, "subsidiary_ref": "0000773", "name": "Kompas Shop Pe Ferneti?i", "address": "Partizanska 150", "city": "Se?ana", "coordinates_x": null, "coordinates_y": null, "phone_number": "+386 57-380-636", "frequency": "A", "channel": "Food", "subchannel": "Supermarket", "customer": 17, "day_planned": true, "badge_visible": true, "open_call": true, "number_of_planned": 13, "number_of_calls": 1, "last_call_day": 1, "notes": null},...
]
What is wrong with my code?
EDIT:
I figure out, that if I create a string variable, and then convert this string value to data it works even with Subsidiary struct.
So something should be wrong with the data variable.

The fact that you can decode a String.self and get your JSON printed means that the root of the JSON is a string. The JSON you got, in text form, probably looks like this:
"[\r\n {\"id\": 5913, \"subsidiary_ref\": \"0000159\", \"name\": \"Mercator Hipermarket Koper\", \"address\": \"Poslov.98-Koper,Dolinska C\", \"city\": \"Koper\", \"coordinates_x\": null, \"coordinates_y\": null, \"phone_number\": \"+386 56-636-800\", \"frequency\": \"A\", \"channel\": \"Food\", \"subchannel\": \"Hypermarket\", \"customer\": 7, \"day_planned\": true, \"badge_visible\": true, \"open_call\": true, \"number_of_planned\": 13, \"number_of_calls\": 22, \"last_call_day\": 3, \"notes\": \" notesi ki ne smejo nikoli zginiti bla marko bdsa\"}\r\n]"
Note that the actual JSON you want is put in ""s, so all the special characters are escaped.
The web service has given the JSON in a very weird way...
To get the actual JSON data decoded, you need to get the string first, then turn the string into Data, then decode that Data again:
// nil handling omitted for brevity
let jsonString = try! JSONDecoder().decode(String.self, from: data)
let jsonData = jsonString.data(using: .utf8)!
let sub = try! JSONDecoder().decode([Subsidiary].self, from: jsonData)
Of course, the best solution is to fix the backend.

Related

Received json data is in form of 531 bytes and giving curruptedData error in swift 4.1 and Xcode 9.2

I am trying to get response from server which is in form of JSON with Swift 4.1 and Xcode 9.2. But the received JSON data by JSONDecoder is printing 531 bytes and giving error
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "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.})))
my request task is
let itemUrl = URL(string: mainUrl.MainUrl + "viewallstore")
let foodUrl = URLRequest(url: itemUrl!)
URLSession.shared.dataTask(with: foodUrl) { (data, response, error) in
print(data ?? "Errrrr")
if data != nil {
do {
let storeDetails = try JSONDecoder().decode(AllStores.self, from: data!)
for store in storeDetails.StoreDetails {
self.foods.append(FoodCourt(storeId: store.str_id, storeNo: store.str_number, storeName: store.str_name, storePhone: store.str_phone, storeParking: store.str_parking, storeOpenTime: store.str_open_time, storeCloseTime: store.str_close_tie, storeStartdate: store.str_open_date, storeCloseDate: store.str_close_date, storeLogoPath: store.str_logo_path, storeLogoFileName: store.str_logo_file_name, storeStatus: store.status, storeCategory: store.str_cat, createdAt: store.create_at, storeZone: store.str_zone, storeAvailability: store.str_availability, storeMulCategory: store.str_multiple_cat))
}
}catch let error {
self.toastNeck(message: "\(error)")
print(error)
DispatchQueue.main.async{
myActivityIndicator.stopAnimating()
myActivityIndicator.hidesWhenStopped = true
}
}
DispatchQueue.main.async {
self.collectionViewFoodCourt.reloadData()
}
}
}.resume()
}
and my struct is
struct AllStores: Decodable{
let StoreDetails: [StoreDetail]
}
struct StoreDetail: Decodable {
let str_id: String
let str_number: String
//let tid : String? //tid
let str_name: String
let str_phone: String
let str_parking: String
let str_open_time: String
let str_close_tie: String
let str_open_date: String
let str_close_date: String
let str_logo_path: String
let str_logo_file_name: String
let status: String
let str_cat: String
let create_at: String
let str_zone: String
let str_availability: String
let str_multiple_cat: String
enum CodingKey: String {
case str_id = "str_id"
case str_number = "str_number"
//case tid = "tid"
case str_name = "str_name"
case str_phone = "str_phone"
case str_parking = "str_parking"
case str_open_time = "str_open_time"
case str_close_tie = "str_close_tie"
case str_open_date = "str_open_date"
case str_close_date = "str_close_date"
case str_logo_path = "str_logo_path"
case str_logo_file_name = "str_logo_file_name"
case status = "status"
case str_cat = "str_cat"
case create_at = "create_at"
case str_zone = "str_zone"
case str_availability = "str_availability"
case str_multiple_cat = "str_multiple_cat"
}
}
and my json file on server is
{
"StoreDetails": [
{
"str_id": "1",
"str_number": "0",
"tid": "",
"str_name": "Moti mahal",
"str_des": "",
"str_phone": "9540011581",
"str_add": "",
"str_parking": "Ground Floor",
"str_short_dis": "Moti Mahal",
"str_open_time": "10:00:00",
"str_close_tie": "23:00:00",
"str_open_date": "2017-01-29",
"str_close_date": "2018-01-28",
"str_logo_path": "",
"str_logo_file_name": "Moti mahal.png",
"status": "Y",
"str_cat": "1",
"company_id": "0",
"str_lat": "0",
"str_lon": "0",
"create_at": "2017-02-11 19:45:28",
"create_by": "0",
"str_zone": "1",
"str_floor": "0",
"str_availability": "1",
"str_multiple_cat": "1"
},
{
"str_id": "2",
"str_number": "0",
"tid": "",
"str_name": "Ever Green",
"str_des": "",
"str_phone": "9953487923",
"str_add": "",
"str_parking": "Green Floor",
"str_short_dis": "Snakes",
"str_open_time": "10:00:00",
"str_close_tie": "22:00:00",
"str_open_date": "2017-02-05",
"str_close_date": "2017-12-31",
"str_logo_path": "",
"str_logo_file_name": "Ever Green.jpg",
"status": "N",
"str_cat": "1",
"company_id": "0",
"str_lat": "0",
"str_lon": "0",
"create_at": "2018-01-15 22:20:11",
"create_by": "0",
"str_zone": "1",
"str_floor": "0",
"str_availability": "1",
"str_multiple_cat": "1"
}
]
}
the same json file is working fine in android but not working in iOS. I am stuck here from a very long time.
All the other questions on StackOverflow is stating that my JSON file is not valid but it's working elsewhere.
Note. Feel free to edit my question if there any issues.
I have gone through all the similar questions but none of them is working in my scenario.
solved the issue, just needed to add POST request to the URL
foodUrl.httpMethod = "POST"

Swift Create Json for API REST

I need create a json for send from a API REST:
{
"ownId": "seu_identificador_proprio",
"amount": {
"currency": "BRL",
"subtotals": {
"shipping": 1000
}
},
"items": [
{
"product": "Descrição do pedido",
"quantity": 1,
"detail": "Mais info...",
"price": 1000
}
],
"customer": {
"ownId": "seu_identificador_proprio_de_cliente",
"fullname": "Jose Silva",
"email": "nome#email.com",
"birthDate": "1988-12-30",
"taxDocument": {
"type": "CPF",
"number": "22222222222"
},
"phone": {
"countryCode": "55",
"areaCode": "11",
"number": "66778899"
},
"shippingAddress": {
"street": "Avenida Faria Lima",
"streetNumber": 2927,
"complement": 8,
"district": "Itaim",
"city": "Sao Paulo",
"state": "SP",
"country": "BRA",
"zipCode": "01234000"
}
}
}
I am confused with the creation..
I try begin with [NSObject:AnyObject]
var d1 : [NSObject:AnyObject] = ["ownId":"seu_identificador_proprio", "customer":""]
let dd1 = ["currency":"BRL"]
let dd2 = ["shipping":"1000"]
let arr = [d1]
let d = try! NSJSONSerialization.dataWithJSONObject(arr, options: NSJSONWritingOptions.PrettyPrinted)
let s = NSString(data: d, encoding: NSUTF8StringEncoding)! as String
print(s)
But I need help!
I have updated your code and added some hints, how can you build the above listed structure. Happy coding!
// Do not use NSObject as key's type
// Keys in a dictionary are usually Strig in every language
var d1: [String: AnyObject] = ["ownId":"seu_identificador_proprio", "customer":""]
// Define the type of your dictionaries, if you dont, in this case it will create a [String:String] dictionary, but you need to insert an array into it
// Make it a var, so you can mutate the container
var dd1: [String: AnyObject] = ["currency":"BRL"]
// Here the type is undefined. Try inserting anything else than String, and see the results
let dd2 = ["shipping":"1000"]
dd1["subtotals"] = dd2
d1["amount"] = dd1
// Build all your dictionaries like i did above, and at the end add all of them into arr
let arr = [d1]
// Do not force try any throwing function in swift - if there is an error, your application will crash
// Use proper error handling - https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html
do {
let d = try NSJSONSerialization.dataWithJSONObject(arr, options: NSJSONWritingOptions.PrettyPrinted)
let s = NSString(data: d, encoding: NSUTF8StringEncoding)! as String
print(s)
} catch {
// Do your error handling here
}

Error while parsing JSON "Unescaped control character around character 981"

Error Domain=NSCocoaErrorDomain Code=3840 "Unescaped control character
around character 981." UserInfo={NSDebugDescription=Unescaped control
character around character 981.}
I am getting the above error in response to a request.
Below are the lines of code:
Alamofire.request(.POST, urlStr, parameters: parameter, encoding: .JSON, headers: nil).validate().responseJSON {
response in switch response.result {
case .Success(let JSON):
completionHandler(JSON as! NSDictionary)
case.Failure(let Error):
print(Error)
}
}
It gives a JSON response in Postman.
The response which is I am getting in Postman:
{
"orderdetails": {
"status_code": "200",
"status_message": "Order details",
"billingandshipping": {
"billing": {
"firstname": "first",
"lastname": "last",
"email": "aa#bbb.com",
"address": "dasdesfrew",
"city": "Rajkot",
"area": "University Road",
"pincode": "360003",
"phone": "1234567890",
"mobileno": "1234567891"
},
"shipping": {
"firstname": "first",
"lastname": "last",
"email": "aa#bbb.com",
"address": "dasdesfrew",
"city": "dasdesfrew",
"area": "dcdc",
"pincode": "360003",
"phone": "1234567890",
"mobileno": "1234567891"
}
},
"orders": [
{
"order_id": "77",
"order_date": "09-08-2016 13:05:29",
"delivery_date": "10-08-2016",
"order_items": [
{
"Sr": "1",
"product_name": "Lemon",
"gujtitle": "લીંબુ ",
"product_code": "000057",
"product_price": "108.00",
"product_qty": "2",
"unit": "1 kg.",
"product_total": "216"
}
],
"final_total": "216.00",
"shipping_cost": "0.00",
"order_total": "216.00",
"discount_type": "null",
"discount_amount": "null",
"coupon_name": "null",
"comment": "gdhdj\nfghd.g\nghj\n\n\n\n\n\n\n\n\n\n.."
}
]
}
}
As per you were told, there is a problem related to "\n".
So I suggest you can add "" which will work for you, like below:
"\n"=> "\\n"
Because this is a special character called a backspace character.
Since Swift 5, instead of manually adding another \ to your otherwise valid \n in a JSON string, you could simply declare it as a raw string literal, using this syntax:
let jsonString = #"{"comment": "gdhdj\nfghd.g\nghj\n\n\n\n\n\n\n\n\n\n.."}"#
Multiline works too:
let jsonString = #"""
{
"comment": "gdhdj\nfghd.g\nghj\n\n\n\n\n\n\n\n\n\n.."
}
"""#
While the above would compile fine if using just """ (without the #), in runtime it would throw an error in the example below with JSONSerialization, which is fixed by #""":
do {
guard let data = jsonString.data(using: .utf8) else { throw SomeError() }
let obj = try JSONSerialization.jsonObject(with: data)
print("valid!")
} catch {
print(error)
}
NSLog the NSData that you received and have a look what you find around byte 981. The thing with unescaped control characters is that they are invisible, so you can't see them in an NSString, but you'll see them in the NSData.
If your data has length 981 bytes or very close then there's a chance that your code processed incomplete JSON data which will almost always fail; that's something you need to fix. If there is a control character between some items (say between two array elements) then this might be a bug in the server code.
I spent some time to figure out what 49546 was.
If your issue is Unescaped control character around character 49546, replace \t with \\\t.
To be sure (as people make foul copy/paste...), I build my object safe:
...
private final func fillWith(
id: Int,
name: String?
) {
self.id = id
self.productName = name?.replacingOccurrences(of: "\t", with: "")
self.productName = self.productName?.replacingOccurrences(of: "\n", with: "")
So no problem when sending up.

How to create this JSON Object in Swift

I have this JSON file which I want to create and send to a server. I am new to using JSON so please can someone guide me how can I create this Object ?
{
"today_steps": 5663,
"activities": [{
"activity_name": "Walking",
"start_datetime": "2016-07-03 10:03AM",
"activity_duration": 2768000,
"steps": 1362,
"average_heart": 96,
"calories": 109
}, {
"activity_name": "Running",
"start_datetime": "2016-07-03 02:45PM",
"activity_duration": 1768000,
"steps": 2013,
"average_heart": 112,
"calories": 271
}],
"workout": []
}
Try code
Swift 2
let activities = [["activity_name":"Walking",
"start_datetime":"2016-07-03 10:03AM",
"activity_duration":2768000,
"steps":1362,
"average_heart":96,
"calories":109],
["activity_name":"Running",
"start_datetime":"2016-07-03 02:45PM",
"activity_duration":1768000,
"steps":2013,
"average_heart":112,
"calories":271]]
let dictionary = ["today_steps":5663,
"activities":activities,
"workout":[]
]
print(dictionary)
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(dictionary, options: NSJSONWritingOptions.PrettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
} catch let error as NSError {
print(error)
}
Swift3
change convert dictionary to Json by
let jsonData = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted)
You don't always need to create an object to send it to server. You can also create a request with the required headers and you can send it as a string.
Take a look at this library and its documentation.
https://github.com/Alamofire/Alamofire
You can also send NSDictionary and this library will convert that to a JSON object.
An example from their github.
let parameters = [
"foo": [1,2,3],
"bar": [
"baz": "qux"
] ]
Alamofire.request(.POST, "https://httpbin.org/post", parameters: parameters, encoding: .JSON)
// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
Try use this
let json = [
"today_steps": 5663,
"activities": [[
"activity_name": "Walking",
"start_datetime": "2016-07-03 10:03AM",
"activity_duration": 2768000,
"steps": 1362,
"average_heart": 96,
"calories": 109
], [
"activity_name": "Running",
"start_datetime": "2016-07-03 02:45PM",
"activity_duration": 1768000,
"steps": 2013,
"average_heart": 112,
"calories": 271
]],
"workout": []
]
You can go ahead and use the NSJSONSerialization class to convert the data to an object that can be later be parsed.
A method like the following can always be used.
// Given raw JSON, return a usable Foundation object
private func convertDataWithCompletionHandler(data: NSData, completionHandlerForConvertData: (result: AnyObject!, error: NSError?) -> Void) {
var parsedResult: AnyObject!
do {
parsedResult = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
} catch {
let userInfo = [NSLocalizedDescriptionKey : "Could not parse the data as JSON: '\(data)'"]
completionHandlerForConvertData(result: nil, error: NSError(domain: "convertDataWithCompletionHandler", code: 1, userInfo: userInfo))
}
completionHandlerForConvertData(result: parsedResult, error: nil)
}
Pass the JSON result given by the api call to this method.

Parser to json JSON array using swift

I am doing an IOS (swift) app that communicates with a server.
Response from server is a JSON array which contains some JSON objects.
When i show by console string response is fine:
[{"NAME":"","SURNAME":"","ID":5,"USERNAME":"dpcabo2"},{"NAME":"","SURNAME":"","ID":10,"USERNAME":"default"},{"NAME":"","SURNAME":"","ID":11,"USERNAME":"esteban"}]
But when i try parser this info to json using this:
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(self.my_data!,
options:NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
println("\(jsonResult)")
app crashes.
So i need a solution to parser correctly this information.
Ps.self.my_data! is a NSMutableData.
Thanks
You can use below code for parsing. It prints and parse you object properly.
import Foundation
let jsonObject: [AnyObject] = [
["NAME":"","SURNAME":"","ID":5,"USERNAME":"dpcabo2"],
["NAME":"","SURNAME":"","ID":10,"USERNAME":"default"],
["NAME":"","SURNAME":"","ID":11,"USERNAME":"esteban"]
]
let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions(0), error: nil)
Response is as below:
[["USERNAME": "dpcabo2", "ID": 5, "NAME": "", "SURNAME": ""],
["USERNAME": "default", "ID": 10, "NAME": "", "SURNAME": ""],
["USERNAME": "esteban", "ID": 11, "NAME": "", "SURNAME": ""]]

Resources