Parsing JSON Response with Alamofire in Swift - ios

When using the Alamofire Framework, my responses don't seem to be getting parsed correctly. The JSON response I get has some keys that appear to not be strings, and I don't know how to reference them/get their values.
Here is the part of my code that makes the call:
var url = "http://api.sandbox.amadeus.com/v1.2/flights/low-fare-search"
var params = ["origin": "IST",
"destination":"BOS",
"departure_date":"2014-10-15",
"number_of_results": 1,
"apikey": KEY]
Alamofire.request(.GET, url, parameters: params)
.responseJSON { (_, _, json, _) in
println(json)
}
}
And here is the first section printout when that function is called
Optional({
currency = USD;
results = ({
fare = {
"price_per_adult" = {
tax = "245.43";
"total_fare" = "721.43";
};
restrictions = {
"change_penalties" = 1;
refundable = 0;
};
"total_price" = "721.43";
};
...
});
});
You'll notice that results is not "results", but "price_per_adult" is the correct format. Is there some step I'm missing? When I cast it to NSDictionary it doesn't do anything to help the key format either.
I also tried the same endpoint in javascript and ruby, and both came back without problem, so I'm fairly confident that it is not the API that is causing problems.

Those keys are still Strings, that's just how Dictionarys are printlnd. It looks like it will surround the String in quotes when printing it only if it contains non-alphanumeric characters (_ in this case). You can test this by manually creating a Dictionary similar to the one you're getting back from your API request and then printing it:
let test = [
"currency": "USD",
"results": [
[
"fare": [
"price_per_adult": [
"tax": "245.43",
"total_fare": "721.43"
],
"restrictions": [
"change_penalties": 1,
"refundable": 0
],
"total_price": "721.43"
]
]
]
]
println(test)
Outputs:
{
currency = USD;
results = (
{
fare = {
"price_per_adult" = {
tax = "245.43";
"total_fare" = "721.43";
};
restrictions = {
"change_penalties" = 1;
refundable = 0;
};
"total_price" = "721.43";
};
}
);
}

Related

How to get one value from a Codable struct with multiple values?

I have a large JSON response and inside
{
"availabilityResultList": [
{
"availabilityRouteList": [
{
"availabilityByDateList": [
{
"originDestinationOptionList": [
there are 3 separate
"originDestinationOptionList": [
{
"fareComponentGroupList":[...]
},
{
"fareComponentGroupList":[...]
},
{
"fareComponentGroupList":[...]
},
],
I can access the values in the first 'fareComponentGroupList' with Codables
as
root.availabilityResultList.first?.availabilityRouteList.first?.availabilityByDateList.first?.originDestinationOptionList.first?.fareComponentGroupList.first?.xxx
How do I access values the second and third fareComponentGroupList ?
(I am sorry about these silly questions but I am new with swift Codables)
Since originDestinationOptionList returns an array of dictionary, just fetch it from there by index.
let originDestinationOptionList = root.availabilityResultList.first?.availabilityRouteList.first?.availabilityByDateList.first?.originDestinationOptionList
let firstobject = originDestinationOptionList[0]["fareComponentGroupList"]
let secondObject = originDestinationOptionList[1]["fareComponentGroupList"]
let firstObjectsFirstItem = firstObject[0]
If the above gives error, this works ( Swift 5)
let originDestinationOptionList = root.availabilityResultList.first?.availabilityRouteList.first?.availabilityByDateList.first?.originDestinationOptionList
let firstobject = originDestinationOptionList[0].fareComponentGroupList.first
let firstObjectsFirstItem = firstObject?. (add the remaining part)

swift parse json as per maintaining order

Suppose i have json string in which there is a json array called data.
The array holds json object of user profile data for example name,age,gender etc.
Now want to parse that json object as per order, for example if the object is
{
"name": "sample name",
"age": "30",
"gender": "male"
}
i want to parse the list as ordered like name,age,gender but with ios,when i convert the json object as dictionary , the order is changed,i know dictionary is not ordered so what is the the alternative to achieve this?
its a third party api so i dont have any hand on it,we have done it in android with linked hash map,but really stuck in swift , the last thing i would want to do is parse with regular expression.
im parsing the json in following way :
var rootData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
if let val = fromList["data"] {
let dataNode = val as! [[String:Any]]
for row in dataNode {
for (key,keyVal) in row {
//here the key is not in order.because when we cast it as dictionary the order gets changed.
}
}
For android we have achieved to do this with following function :
public ArrayList<LinkedHashMap<String, Object>> parseJsonArrayList(String odata, String arrayName) {
ArrayList<LinkedHashMap<String, Object>> mylist = new ArrayList<>();
try {
JSONObject e = new JSONObject(odata);
JSONArray data = e.getJSONArray(arrayName);
for(int i = 0; i < data.length(); ++i) {
JSONObject v = data.getJSONObject(i);
LinkedHashMap<String, Object> map = new LinkedHashMap<>(100, 0.75f, false);
Iterator keys = v.keys();
while(keys.hasNext()) {
String key = String.valueOf(keys.next());
//gph.log("debug4", key);
map.put(key, v.getString(key));
//gph.log("debug4", v.getString(key));
}
mylist.add(map);
}
} catch (JSONException var10) {
var10.printStackTrace();
}
return mylist;
}
Don’t try to order the dictionary. Instead create an array of the keys in the order you desire:
let keys = [“name”, “age”, “gender”]
Then access the dictionary with them:
for key in keys {
let value = dict[key]
// Present the value.
}
That will ensure the order you expect.
As you mentioned you cannot get the ordered data in Dictionary. If possible you can add the "order" key in your JSON like
[
{
"name": "sample name",
"order": 1
},
{
"age": "30",
"order": 1
},
{
"gender": "",
"male": "",
"order": 1
}
]
so that based on the order key you can do the sorting.

Why SwiftyJSON cannot parse Array String in swift 3

{
"item": [
{
"pid": 89334,
"productsname": "Long Way",
"address": "B-4/7, Malikha Housing, Yadanar St., Bawa Myint Ward,",
"telephone": "[\"01570269\",\"01572271\"]"
},
{
"pid": 2,
"productsname": "Myanmar Reliance Energy Co., Ltd. (MRE)",
"address": "Bldg, 2, Rm# 5, 1st Flr., Hninsi St., ",
"telephone": "[\"202916\",\"09-73153580\"]"
}
],
"success": true
}
I cannot parse telephone value from above JSON object with following code.
for item in swiftyJsonVar["item"].array! {
if let jsonDict = item.dictionary {
let pid = jsonDict["pid"]!.stringValue
let productsname = jsonDict["productsname"]!.stringValue
var telephones = [String]()
for telephone in (jsonDict["telephone"]?.array)! {
telephones.append(telephone.stringValue)
}
}
}
I want to get and display one by one phone number of above JSON. I'm not sure why above code is not working. Please help me how to solve it, thanks.
Because telephone is a string that looks like an array, not an array itself. The server encoded this array terribly. You need to JSON-ify it again to loop through the list of telephone numbers:
for item in swiftyJsonVar["item"].array! {
if let jsonDict = item.dictionary {
let pid = jsonDict["pid"]!.stringValue
let productsname = jsonDict["productsname"]!.stringValue
var telephones = [String]()
let telephoneData = jsonDict["telephone"]!.stringValue.data(using: .utf8)!
let telephoneJSON = JSON(data: telephoneData)
for telephone in telephoneJSON.arrayValue {
telephones.append(telephone.stringValue)
}
}
}

Json response gets only last row in swift

hi i am trying to get the son response from the server and the following is the response i get from server while using postman
{
"stories": [
{
"image_url": "http://storybox.dk/wp-content/uploads/2016/11/Hansa.jpg",
"name": "New story",
"story_key": "ahJlfnN0b3J5Ym94LWJhY2tlbmRyEgsSBVN0b3J5GICAgIC8oYIKDA"
},
{
"image_url": "http://storybox.dk/wp-content/uploads/2016/11/Hansa.jpg",
"name": "Story of Foo the fox.",
"story_key": "ahJlfnN0b3J5Ym94LWJhY2tlbmRyEgsSBVN0b3J5GICAgID4woQKDA"
}]
}
but when i try to get the values inside the stories tag as array it gets only one value as
self.StoryListArr = (reponsedict.value(forKey: "stories") as? NSArray)!
(
{
"image_url" = "http://storage.googleapis.com/storybox-backend.appspot.com/5649391675244544/Screenshot_from_2017-03-16_113338.png";
name = "Story of Foo";
"story_key" = ahJlfnN0b3J5Ym94LWJhY2tlbmRyEgsSBVN0b3J5GICAgID4woQKDA;
}
)
please advice how to deal this
when i try to print response dict it shows only one value in it
{
stories = (
{
"image_url" = "http://storage.googleapis.com/storybox-backend.appspot.com/5649391675244544/Screenshot_from_2017-03-16_113338.png";
name = "Story of Foo";
"story_key" = ahJlfnN0b3J5Ym94LWJhY2tlbmRyEgsSBVN0b3J5GICAgID4woQKDA;
}
);
}
There is no reason to use NSArray in Swift. Simply cast the object to [Dictionary].
if let stories = responsedict["stories"] as? [Dictionary<String,String>] {
print(stories)
}

Sending array of dictionaries with alamofire

I have to send array of dictionaries via POST request. For example:
materials: [[String, String]] = [
[
"material_id": 1,
"qty": 10
],
[
"material_id": 2,
"qty": 5
]
]
Alamofire.request sends the next post data:
materials => array(
[0] => array("material_id" => 1),
[1] => array("qty" => 10),
[2] => array("material_id" => 2),
[3] => array("qty" => 5),
)
I want receive that representation:
materials => array(
[0] => array(
"material_id" => 1,
"qty" => 10
),
[1] => array(
"material_id" => 2,
"qty" => 5
),
)
The problem was in append method. I have coded on PHP 5 years and forgoted that in Swift the indexes not automatically assigned like in PHP. So, my first bugged code was:
func getParameters() -> [[String: AnyObject]] {
var result = [[String: AnyObject]]()
for mmap in mmaps {
let material: [String: AnyObject] = [
"material_id": mmap.material.id,
"quantity": mmap.qty
]
result.append(material)
}
return result
}
The answer is hard assign the keys as you need:
func getParameters() -> [String: [String: AnyObject]] {
var result = [String: [String: AnyObject]]()
let mmaps = self.mmaps.allObjects as [Mmap]
for i in 0..<mmaps.count {
let mmap = mmaps[i]
let material: [String: AnyObject] = [
"material_id": mmap.material.id,
"quantity": mmap.qty
]
result["\(i)"] = material
}
return result
}
A couple of thoughts:
It would be easiest if you sent the response as a dictionary with one key, and it will correctly encode the array within the dictionary:
let materials = [ "materials":
[
[
"material_id": 1,
"qty": 10
],
[
"material_id": 2,
"qty": 5
]
]
]
You could then just supply that as the parameters of request(), and Alamofire will properly encode that for you.
If you wanted to send an array of dictionaries, an alternative would be to change the web service to accept JSON. You could then encode the JSON yourself (using JSONSerialization or JSONEncoder), set the body of the request, and then send that request.
If you want to send application/x-www-form-urlencoded request with the array of dictionaries, you'd have to encode that yourself. In Swift 3 and later, that might look like:
func encodeParameters(_ object: Any, prefix: String? = nil) -> String {
if let dictionary = object as? [String: Any] {
return dictionary.map { key, value -> String in
self.encodeParameters(value, prefix: prefix != nil ? "\(prefix!)[\(key)]" : key)
}.joined(separator: "&")
} else if let array = object as? [Any] {
return array.enumerated().map { (index, value) -> String in
return self.encodeParameters(value, prefix: prefix != nil ? "\(prefix!)[\(index)]" : "\(index)")
}.joined(separator: "&")
} else {
let escapedValue = "\(object)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
return prefix != nil ? "\(prefix!)=\(escapedValue)" : "\(escapedValue)"
}
}
Where
extension CharacterSet {
/// Returns the character set for characters allowed in the individual parameters within a query URL component.
///
/// The query component of a URL is the component immediately following a question mark (?).
/// For example, in the URL `http://www.example.com/index.php?key1=value1#jumpLink`, the query
/// component is `key1=value1`. The individual parameters of that query would be the key `key1`
/// and its associated value `value1`.
///
/// According to RFC 3986, the set of unreserved characters includes
///
/// `ALPHA / DIGIT / "-" / "." / "_" / "~"`
///
/// In section 3.4 of the RFC, it further recommends adding `/` and `?` to the list of unescaped characters
/// for the sake of compatibility with some erroneous implementations, so this routine also allows those
/// to pass unescaped.
static var urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: generalDelimitersToEncode + subDelimitersToEncode)
return allowed
}()
}
Obviously, use whatever response method is appropriate for the nature of your server's response (e.g. response vs. responseJSON vs. ...).
Anyway, the above generates a request body that looks like:
materials[0][material_id]=1&materials[0][qty]=10&materials[1][material_id]=2&materials[1][qty]=5
And this appears to be parsed by servers as you requested in your question.
It's worth noting that this final point illustrates the preparation of an application/x-www-form-urlencoded request with nested dictionary/array structure, as contemplated here. This works on my server run by a major ISP, but I must confess that I haven't seen this convention documented in formal RFCs, so I'd be wary of doing it. I'd personally be inclined to implement this as JSON interface.
For prior versions of Swift, see previous revision of this answer.
You can make your array as JSON string and post in to server, then parse JSON in server end and from that you can get your desired data, lke this:
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject: yourArry options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
Hope this helps.. :)

Resources