swift vapor json response to array - arrays

using swift vapor and elasticsearch, got a response like:
{
"_shards": {
"failed": 0,
"successful": 5,
"total": 5
},
"hits": {
"hits": [
{
"_id": "3",
"_index": "items_v1",
"_score": 1.2029922,
"_source": {
"property1": "test",
"property2": "another test",
...
},
"_type": "item"
},
...
inside "hits" -> "hits" -> "_source" I got all the properties of my model "Item". How can I create an array of Items "[Item]" from this json response?

Small enhancement, use a guard statement to avoid the nested ifs...
guard
let dict = response as? [String : Any],
let hits = dict["hits"] as? [String : Any],
let hitArray = hits["hits"] as? [[String : Any]]
else
{ throw Abort}
for hit in hitArray {
if let source = hit["_source"] {
arrayOfItems.append(Item(with: source))
}
}

Parse your response in this way, so there will be no crashes if some value will not be sent.
if let dict = response as? [String : Any] {
if let hits = dict["hits"] as? [String : Any] {
if let hitArray = hits["hits"] as? [[String : Any]] {
for hit in hitArray {
if let source = hit["_source"] {
arrayOfItems.append(Item(with: source))
}
}
}
}
}
Int your Item class create init method, where you will initialize item's properties.
init(with dict: [String : Any]) {
if let property1 = dict["property1"] as? Int {
self.property1 = property1
}
super.init()
}

Try like this! I assume that you get the Response and that response in saved in response variable
var myarray = [String]()
let hitDict = response["hits"] as! [String:AnyObject]
let hitArray = hitDict["hits"] as! Array
let someDict = hitArray[0] as! [String:AnyObject]
let sourcDict = someDict["_source"] as! [String:AnyObject]
let property1 = sourcDict["property1"] as! String
let property2 = sourcDict["property2"] as! String
myarray.append(property1)
myarray.append(property2)

var myArray = [String:String]()
//response from try drop.client.get(…)
let bodyReceived = responseFirebaseAssigned?.body.bytes
//JSON object made of bodyReceived
let JsonFirebase:JSON?
for val in JsonFirebase?.object ?? [:]{
let valKey = val.key.string
let valValue = val.value.string
arrayFB[valKey!] = valValue
print("arrayFB is \(arrayFB)")
}

Related

How to add data inside two arrays inside for loop

I'm trying to add data to array inside for loop and again to array in another for loop.
Its working on first for loo, but in second data is empty. How to fix that
This is how it currently looks inside firestore and you can see scores missing.
this is code:
func updateCourseData() {
var data = ["endTime": Timestamp(date: Date()),
"players": []] as [String : Any]
for i in 0..<players.count {
var playerData = ["playerId": games[i].id,
"scores": []] as [String : Any]
var existingItems = data["players"] as? [[String: Any]] ?? [[String: Any]]()
existingItems.append(playerData)
data["players"] = existingItems
for score in 0..<selectedPage + 1 {
let scoreData = ["hole": games[i].scores[score].hole,
"score": games[i].scores[score].score] as [String : Any]
var existingScores = playerData["scores"] as? [[String: Any]] ?? [[String: Any]]()
existingScores.append(scoreData)
playerData["scores"] = existingScores
}
}
Constants.FirebaseCollection.gamesCollection.document(documentId).updateData(data) { error in
if let error = error {
print(error.localizedDescription)
}
print("Game updated")
}
}

Swift3 get element from array

I have a json string converted to string array like below:
let str = "{ \"dtResult\": [ { \"itmdtl_item_no\": \"AO406705959SE3\" }, { \"itmdtl_item_no\": \"AO406708959SE3\" } ] }"
let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
let result = json["dtResult"] as? [[String:Any]] ?? [ ]
let item = result[0] as! [String:Any]
print(item)
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
When i print out the result of item, i got the value like this:
["itmdtl_item_no": AO406705959SE3]
But i just want the string "AO406705959SE3", how can i do? Thanks.
First of all don't write
let result = json["dtResult"] as? [[String:Any]] ?? [ ]
If result is nil or empty the app will crash on result[0]
Instead write to check if the array exists and is not empty
if let result = json["dtResult"] as? [[String:Any]], !result.isEmpty {
let item = result[0] as! [String:Any]
// Now get the value for key "itmdtl_item_no"
if let itemNo = item["itmdtl_item_no"] as? String {
print(itemNo)
}
}

Swift 3 JSON Array to Dictionary

I have a JSON result from Alamofire and SwiftyJSON and am trying to create a dictionary from it to create an array
JSON Result
JSON: [
{
"p_589b6a49a0bfd" : {
"path" : "\/uploads\/588fa43eba3c9\/588fa43eba3c9_1486580297.jpg",
"likes" : "0",
"userid" : "588fa43eba3c9",
"caption" : "Bae",
"comments" : "0",
"date" : "1486580297"
}
},
{
"p_589b7f1c540f1" : {
"path" : "\/uploads\/588fa43eba3c9\/588fa43eba3c9_1486585628.jpg",
"likes" : "0",
"userid" : "588fa43eba3c9",
"caption" : "Hot stuff bitch ",
"comments" : "0",
"date" : "1486585628"
}
}
]
Request/Response
Alamofire.request(BASE_URL + "index.php/feed/build", method: .get, headers: headers).responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
case .failure(let error):
print(error)
}
}
I have then set up a simple class called 'FeedPost' which will store each of the elements in JSON response (this is the function in the FeedPost class)
init(postid: String, postData: Dictionary<String, AnyObject>) {
self._postid = postid
if let caption = postData["caption"] as? String {
self._caption = caption
}
if let path = postData["path"] as? String {
self._path = path
}
if let likes = postData["likes"] as? Int {
self._likes = likes
}
if let comments = postData["comments"] as? Int {
self._comments = comments
}
if let userid = postData["userid"] as? String {
self._userid = userid
}
if let date = postData["date"] as? String {
self._date = date
}
}
I need to somehow cycle through the JSON to create a dictionary to pass to FeedPost then add each FeedPost to another array called Posts during the request. The string that starts with 'p_' I want to use as the postid
With Alamofire response you have used SwiftyJSON and with your FeedPost init you are using swift native dictionary. So I'm suggesting you to either work with SwiftyJSON or with swift's native type. Since you have already added init with dictionary I'm answering your answer with native type.
Alamofire.request(BASE_URL + "index.php/feed/build", method: .get, headers: headers).responseJSON { response in
switch response.result {
case .success(let value):
If let dic = value as? [String: Any],
let array = DIC["JSON"] as? [[String: Any]] {
for item in array {
for (key, value) in item {
If let subDic = value as? [String: Any] {
let obj = FeedPost(postid: key, postData: subDic)
}
}
}
}
print("JSON: \(json)")
case .failure(let error):
print(error)
}
}
Note: Correct Dictionary notation for JSON in swift 3 is [String: Any] not [String: AnyObject] so change your init parameter postData's type to [String: Any].
Below is the code which can be used for your case, This code is copied from playgrounds.
import UIKit
typealias JSONDictionary = [String: AnyObject]
class Post {
let id: String
let userId: String?
let date: Double?
let caption: String?
let comments: Double?
let likes: Double?
let path: String?
init?(with dictionary: JSONDictionary) {
guard let postId = dictionary.keys.first, let postInfo = dictionary[postId] as? JSONDictionary else { return nil }
self.id = postId
self.userId = postInfo["userid"] as? String
self.date = postInfo["date"] as? Double
self.caption = postInfo["caption"] as? String
self.comments = postInfo["comments"] as? Double
self.likes = postInfo["likes"] as? Double
self.path = postInfo["path"] as? String
}
}
Parsing JSON array will be like this.
case .success(let value):
let jsonArray = value["JSON"] as? [JSONDictionary]
let posts = jsonArray?.flatMap(Post.init(with:))
print("Posts \(posts)"
case .failure: break
I have tried this using a local JSON file in a Playgrounds & code was something like this.
let url = Bundle.main.url(forResource: "data", withExtension: "json")
let data = try! Data(contentsOf: url!)
let jsonArray = try! JSONSerialization.jsonObject(with: data , options: .allowFragments) as? [JSONDictionary]
let posts = jsonArray?.flatMap(Post.init(with:))

serializing array to object in swift

i have the following object class:
class NewsItem: NSObject {
var storyCategory: String?
var titleText: String?
var paragraph1: String?
var paragraph2: String?
var featureImage: String?
var secondImage: String?
var storyDate: String?
var majorReference: String?
var fact: String?
var actualFeatureImage: UIImage? // Using these two to pass the image from home to story view
var actualSecondImage: UIImage?
var referencesArray = [AnyObject]()
...
init?(dictionary: [String: AnyObject]) {
guard
let storyCategory = dictionary["category"] as? String,
let titleText = dictionary["title"] as? String,
let paragraph1 = dictionary["paragraph1"] as? String,
let paragraph2 = dictionary["paragraph2"] as? String,
let featureImage = dictionary["headerImage"] as? String,
let storyDate = dictionary["date"] as? String,
let majorReference = dictionary["majorReference"] as? String,
let secondImage = dictionary["secondImage"] as? String
else {
return nil
}
self.storyCategory = storyCategory
self.titleText = titleText
self.paragraph2 = paragraph2
self.paragraph1 = paragraph1
self.featureImage = featureImage
self.storyDate = storyDate
self.majorReference = majorReference
self.fact = dictionary["fact"] as? String //if there's a fact in the dict, it will be assigned and if not there'll be nil
self.secondImage = secondImage
let referenceObject = dictionary["reference"] as? [[String: AnyObject]]
for object in referenceObject! {
self.referencesArray.append(object)
//print(object)
}
//bellow is a snippet from the method that's serializing the JSON data
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
if let newsArticles = json["stories"] as? [[String: AnyObject]] {
for article in newsArticles {
let newsArticle = NewsItem(dictionary: article)
newsItems.append(newsArticle!)
}
}
} catch {
print("error in the news items delivery: \(error)")
let displayError = EHPlainAlert(title: "hmmm...", message: "\(error)", type: ViewAlertError)
displayError.show()
}
}
in the backend of the app, i have JSON objects and one of the values of each of the objects is as follows:
... "reference" : [
{
"refName": "CNN",
"refURL": "http://edition.cnn.com/2016/07/19/football/brexit-effect-on-english-premier-league/index.html"
},
{
"refName": "Telegraph",
"refURL": "http://www.telegraph.co.uk/football/2016/06/21/what-would-brexit-mean-for-the-premier-league/"
}
], ...
i don't know if i have the JSON wrong o' something but what i intend to have is an array of references, each reference having a reference name(refName) and the url of the reference (refURL).
my question is, what would be the best method to have the JSON object above be added to the referencesArray in my object class. Would it be better to use a dictionary instead of an array? if so, how should i re-write my code?
if you need any more details about the code, ask and ye shall receive an update. Thanks!
EDIT
Here's the JSON in full :
{
"stories" : [
{
"title" : "English Premier League vs BREXIT: Who will win?",
"headerImage" : "http://i2.cdn.turner.com/cnnnext/dam/assets/160117112140-rooney-goal-exlarge-169.jpg",
"category" : "Sports",
"paragraph1" : "Paragraph text",
"paragraph2" : "More text goes here",
"date" : "21st July",
"majorReference" : "CNN",
"reference" : [
{
"refName": "CNN",
"refURL": "http://edition.cnn.com/2016/07/19/football/brexit-effect-on-english-premier-league/index.html"
},
{
"refName": "Telegraph",
"refURL": "http://www.telegraph.co.uk/football/2016/06/21/what-would-brexit-mean-for-the-premier-league/"
}
],
"secondImage" : "http://www.telegraph.co.uk/content/dam/football/2016/06/14/02-leicester-afp-xlarge_trans++Wy_u4a9GUNQgLIY2EGV3qvLCN329DeTLuwi-bwi35Bo.jpg",
"fact" : "Talent will go missing"
}, ...
Thats just one story... after the comma, is another story.
First, define a data model for your reference:
struct NewsItemReference {
var refName: String
var refURL: NSURL
}
Then modify your NewsItem class as below:
class NewsItem: NSObject {
var referencesArray = [NewsItemReference]()
init?(dictionary: [String: AnyObject]) {
...
if let references = dictionary["reference"] as? [[String: AnyObject]] {
for object in references {
guard let refName = object["refName"] as? String,
let refURLString = object["refURL"] as? String,
let refURL = NSURL(string: refURLString) else {
continue
}
self.referencesArray.append(NewsItemReference(refName: refName, refURL: refURL))
}
}
}
If you see yourself quickly outgrowing this model (the code is too verbose), take a look at some JSON-to-object mapping framework like ObjectMapper.

why the error: fatal error: Array index out of range? [swift]

Please find error location in the below image :
The number of values in tripOption will change in each request.
There a logic problem in the code where the tripOption for example outputs just 2 values.. but the loop keeps going and says array out of index.. i have no idea how to fix this issue.
var arrayOfFlights : [FlightDataModel] = [FlightDataModel]()
if json != nil {
//insert airline data into arrayOfFlights
if let myJSON = json as? [String:AnyObject] {
if let trips = myJSON["trips"] as? [String:AnyObject] {
if let data = trips["data"] as? [String:AnyObject] {
if let carriers = data["carrier"] as? [[String:String]] {
for (index, carrierName) in enumerate(carriers) {
var myFlight = FlightDataModel(airline: carrierName["name"] as String!, price:nil)
self.arrayOfFlights.append(myFlight)
println("\(self.arrayOfFlights[index].airline!)")
}
}
}
if var tripOptions = trips["tripOption"] as? [[String:String]] {
for (index, tripOption) in enumerate(tripOptions) {
self.arrayOfFlights[index].price = tripOption["saleTotal"] as String!
println("price \(self.arrayOfFlights[index].price!)")
}
}
}
}
parameteers in url jsjon request:
var parameters = [
"request": [
"slice": [
[
"origin": from,
"destination": to,
"date": when
]
],
"passengers": [
"adultCount": 1,
"infantInLapCount": 0,
"infantInSeatCount": 0,
"childCount": 0,
"seniorCount": 0
],
"solutions": 5,
"refundable": false
]
]
The error is because you are trying to access an element in arrayOfFlights with an index greater than its size - 1.
// index > no of existing elements in the array
self.arrayOfFlights[index].price = tripOption["saleTotal"] as String!
Maybe you are trying to push new elements into an empty array?
self.arrayOfFlightPrices.append(tripOption["saleTotal"] as String!)
You should simplify your code and perhaps use the first iteration of the results to ensure you don't go out of bounds. Something LIKE this:
var arrayOfFlights : [FlightDataModel] = [FlightDataModel]()
if let data = json as? NSDictionary {
if let carriers = data.valueForKeyPath("trips.data.carrier") as? NSArray {
for (index, carrier) in enumerate(carriers) {
var myFlight = FlightDataModel(airline: carrier["name"] as String!, price:nil)
arrayOfFlights.append(myFlight)
}
}
if let trips = data.valueForKey("trips") as? NSArray where arrayOfFlights.count > 0 {
for (index, carrier) in enumerate(arrayOfFlights) {
carrier.price = trips.objectAtIndex(index)["saleTotal"] as String!
}
}
}

Resources