How do I search a very very large json file? - ios

I have a enormous json file and I am trying to extract information form the file, but it is way to big to track down the path. Could I filter it using the id? JSON code I need to pick up course names i.e.
let urlString = "Can't provide the url"
if let url = NSURL(string: urlString){
if let data = try? NSData(contentsOfURL: url, options: []){
let json = JSON(data: data)
parseJSON(json)
}
}
}
func parseJSON(json: JSON){
for (index: String, subJson: JSON) in json {
}
}

I came up with a depth-first way of finding a given JSON object based on a predicate.
I made it into an extension:
extension JSON {
func find(#noescape predicate: JSON -> Bool) -> JSON? {
if predicate(self) {
return self
}
else {
if let subJSON = (dictionary?.map { $0.1 } ?? array) {
for json in subJSON {
if let foundJSON = json.find(predicate) {
return foundJSON
}
}
}
}
return nil
}
}
For instance, to search for the JSON object with a given id field, such as in the question, you can use the method as such:
let json = JSON(data: data)
let predicate = {
(json: JSON) -> Bool in
if let jsonID = json["id"].string where jsonID == "plnMain_ddlClasses" {
return true
}
return false
}
let foundJSON = json.find(predicate)
In this case, if you need to continue and find the classes you were looking for, you would want:
let classes = foundJSON?["children"].arrayValue.map {
$0["html"].stringValue
}
UPDATE — FIND ALL
func findAll(#noescape predicate predicate: JSON -> Bool) -> [JSON] {
var json: [JSON] = []
if predicate(self) {
json.append(self)
}
if let subJSON = (dictionary?.map{ $0.1 } ?? array) {
// Not using `flatMap` to keep the #noescape attribute
for object in subJSON {
json += object.findAll(predicate: predicate)
}
}
return json
}

Related

Cannot append data to array from GET request

I am trying to load data from a GET request using Alamofire library in swift and cannot append data from the requests. I am trying to populate an array of orders to load into a UITableView.
I have tried a few various ways of solving this issue but nothing is working for me. I have commented out the method I tried because with 2 separate calls to fetchAll...Orders and the second call always overwrites the first and then the tableView is loaded with missing items.
class DrinkOrdersTableViewController: UITableViewController {
var orders: [Order] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Current Orders"
}
override func viewWillAppear(_ animated: Bool) {
// fetchAllBeerOrders { orders in
// self.orders = orders!
// //print("Beer fetch: ", self.orders)
// self.tableView.reloadData()
// }
// fetchAllCocktailOrders { orders in
// self.orders = orders!
// //print("Cocktail fetch: ", self.orders)
// self.tableView.reloadData()
// }
fetchAllBeerOrders { orders in
self.orders.append(orders)
self.tableView.reloadData()
}
fetchAllCocktailOrders { orders in
self.orders.append(orders)
self.tableView.reloadData()
}
}
private func fetchAllCocktailOrders(completion: #escaping([Order]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/orders", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let currentOrders = rawInventory.compactMap { ordersDict -> Order? in
guard let orderId = ordersDict!["id"] as? String,
let orderStatus = ordersDict!["status"] as? String,
var pizza = ordersDict!["cocktail"] as? [String: Any] else { return nil }
pizza["image"] = UIImage(named: pizza["image"] as! String)
return Order(
id: orderId,
pizza: Pizza(data: pizza),
status: OrderStatus(rawValue: orderStatus)!
)
}
completion(currentOrders)
}
}
private func fetchAllBeerOrders(completion: #escaping([Order]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/orders", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let currentOrders = rawInventory.compactMap { ordersDict -> Order? in
guard let orderId = ordersDict!["id"] as? String,
let orderStatus = ordersDict!["status"] as? String,
var pizza = ordersDict!["pizza"] as? [String: Any] else { return nil }
pizza["image"] = UIImage(named: pizza["image"] as! String)
return Order(
id: orderId,
pizza: Pizza(data: pizza),
status: OrderStatus(rawValue: orderStatus)!
)
}
completion(currentOrders)
}
}
As of right now I am getting this error with code above: Cannot convert value of type '[Order]?' to expected argument type 'Order'. The ideal outcome of this code is to have the data that is gathered from each GET request to append to the array of Orders. I have verified that the GET requests are working and giving back the correct data. Please Help :]
You declared orders of type [Order] and your fetch methods compilation blocks return [Order]?. As you can see, you cannot convert value of type [Order]? to expected argument type Order when you wrote self.orders.append(orders).
To fix these, put a guard unwrap in fetch method invocations.
fetchAllBeerOrders { orders in
guard let _orders = orders else { return }
self.orders.append(_orders)
self.tableView.reloadData()
}
fetchAllCocktailOrders { orders in
guard let _orders = orders else { return }
self.orders.append(_orders)
self.tableView.reloadData()
}
Now, you have a potential memory leak in your code. fetchAllBeerOrders and fetchAllCocktailOrders are async methods with compilation blocks. You cannot use a strong reference to self here. Use weak to avoid a memory leak, like:
fetchAllBeerOrders { [weak self] orders in
guard let _orders = orders else { return }
self?.orders.append(_orders)
self?.tableView.reloadData()
}
fetchAllCocktailOrders { [weak self] orders in
guard let _orders = orders else { return }
self?.orders.append(_orders)
self?.tableView.reloadData()
}

Retrieve specific Array from JSON in swift in the form of Array of Keys and Values?

JSON :
{
"11/08/22":[
{
"Bill Gates":"Microsoft",
"Steve Balmer":"Microsoft"
}],
"13/08/22":[
{
"Tim Cook":"Apple",
"Jony Ive":"Apple"
}]
}
Swift Code :
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
print(jsonResult)
//Get Result into Seperate Arrays
let keys = jsonResult.flatMap(){ $0.0 as? String }
let values = jsonResult.flatMap(){ $0.1 as? String }
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
Requirements :
If i pass from program "11/08/22", I should be able to get all keys and values in the form of Array of String of only that array named "11/08/22" .
Better Explanation :
It should go into 11/08/22 and it should retrieve "Bill Gates","Steve Balmer" as Keys and "Microsoft" As a value in two separate arrays
For this example let's use an array to collect the people and a set to collect the companies:
var people: [String] = []
var companies: Set<String> = []
Subscript to the JSON dictionary with your "11/08/22" key and cast the result as an array of dictionaries.
Loop over this array and in the loop, add the keys to the people array and insert the values in the companies set.
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
if let dateContent = jsonResult["11/08/22"] as? [[String:String]] {
for group in dateContent {
people.appendContentsOf(group.keys)
group.values.forEach { companies.insert($0) }
}
}
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
print(people)
print(companies)
Result:
["Steve Balmer", "Bill Gates"]
["Microsoft"]
let keys=jsonResult["11/08/22"]?.allKeys as? [String];
let values=jsonResult["11/08/22"]?.allValues as? [String];
It was as simple as this

How can I save and then load a JSON in NSUserDefaults with SwiftyJSON?

in my iOS project I need to save an entire JSON as user data and then reload it on next app launch.
Squashing it into many values and then recreate the JSON is not an option, I just need some serializable way of saving the entire raw JSON.
I tried to convert it to String by doing json.rawString() and recreate it by passing the obtained string to JSON(string), but it doesn't work.
I'm both astonished by the difficulty of making such a simple thing and by the lack of informations about a thing like this online, so I can not wait to discover how to do that :)
Example:
public func saveJSON(j: JSON) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setValue(j.rawString()!, forKey: "json")
// here I save my JSON as a string
}
public func loadJSON() -> JSON {
let defaults = NSUserDefaults.standardUserDefaults()
return JSON(defaults.valueForKey("json") as! String))
// here the returned value doesn't contain a valid JSON
}
Thank you for your answers but they didn't solve my problem. I finally found the solution, which was really simple in facts:
public func loadJSON() -> JSON {
let defaults = NSUserDefaults.standardUserDefaults()
return JSON.parse(defaults.valueForKey("json") as! String))
// JSON from string must be initialized using .parse()
}
Really simple but not documented well.
Swift 5+
func saveJSON(json: JSON, key:String){
if let jsonString = json.rawString() {
UserDefaults.standard.setValue(jsonString, forKey: key)
}
}
func getJSON(_ key: String)-> JSON? {
var p = ""
if let result = UserDefaults.standard.string(forKey: key) {
p = result
}
if p != "" {
if let json = p.data(using: String.Encoding.utf8, allowLossyConversion: false) {
do {
return try JSON(data: json)
} catch {
return nil
}
} else {
return nil
}
} else {
return nil
}
}
Use this if you using SwiftyJSON.
I used the following code and it works like a charm!
NSString *json = #"{\"person\":{\"first_name\":\"Jim\", \"last_name\":\"Bell\"}} ";
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults objectForKey:#"json"]== nil){
[defaults setObject:json forKey:#"json"];
//[defaults synchronize];
}
else{
NSLog(#"JSON %#", [defaults objectForKey:#"json"]);
}
First try to see whether you can save a hard-coded string to the NSUserDefaults first.
Also try to call a [defaults synchronize]; call when you want to save the data. Although that is NOT required, it might be needed in extreme conditions such as if the app is about to terminate.
to retrieve from UserDefaults
func get(_ key: String)-> JSON? {
if let standard = UserDefaults.standard.data(forKey: key), let data = try? standard.toData() {
return JSON(data)
} else {
return nil
}
}
You should parse everything to Data, in order to save model (Better from JSON / JSONSerialization) to UserDefaults
Coded In Swift 5.x
Swift 4+
A cleaner version to the one provided by Alfi up above, for any else that might need this.
func addUserJSONDataToUserDefaults(userData: JSON) {
guard let jsonString = userData.rawString() else { return }
userDefaults.set(jsonString, forKey: "user")
}
func getCachedUserJSONData() -> JSON? {
let jsonString = userDefaults.string(forKey: "user") ?? ""
guard let jsonData = jsonString.data(using: .utf8, allowLossyConversion: false) else { return nil }
return try? JSON(data: jsonData)
}
Here's a swift example that works
import SwiftyJSON
class Users{
init(){
let yourJSON = {"name":"Deeznuts"}
let response = JSON(yourJSON)
// Store your
let httpMessage = response["name"].stringValue
}
}
I extended Userdefaults and added a new var for easy usage and consistency of my keys.
Here is my code:
extension UserDefaults {
var downloadedMarkersInfo : JSON? {
get {
if let jsonString = defaults.value(forKey: "markers") as? String {
if let json = jsonString.data(using: String.Encoding.utf8, allowLossyConversion: false) {
return try! JSON(data: json)
}
}
return nil
}
set {
if let json = newValue {
let jsonString = json.rawString()!
defaults.setValue(jsonString, forKey: "markers")
}
}
}
}
The usage in my View Controller:
if let jsonData = defaults.downloadedMarkersInfo {
// Your code here.
}
using SwiftyJSON - SWIFT 5
var data = JSON()
if(CustomDefaults().checkObject(key: "list2")){
data = JSON.init(parseJSON: CustomDefaults().getObject(key: "list2") as? String ?? "")
}
else{
var bomb = [JSON]()
bomb.append(["name":"Happy","url":"google.com"])
let finalData = JSON(bomb).rawString() ?? "" //data.rawString() ?? ""
CustomDefaults().setObject(value: finalData, key: "list2")
}

Realm is not updating existent objects

I'm using the Realm to save some objects and the user has the ability to reload them through a network function.
class Task: Object {
dynamic var id: String = ""
dynamic var price: Double = 0
override class func primaryKey() -> String {
return "id"
}
func fillNew(json: JSON) {
self.id = json["id"].stringValue
self.name = json["prive"].doubleValue
}
}
The network function get an array of json to build these objects. Once these new objects are created, I pass them through an add or update but when I fetch them again the fields have not changed.
func getPendingTasks(completion: (JSON?, NSError?) -> ()) {
let urlString = ".."
Alamofire.request(.GET, urlString).responseJSON { response in
let json = response.result.value != nil ? JSON(response.result.value!) : nil
dispatch_async(dispatch_get_main_queue()) {
completion(json, response.result.error)
}
}
}
func getTasks() {
self.getPendingTasks() { json, error in
if let error = error {
return
}
let jsonTasks = json!["tasks"].arrayValue
for jsonTask in jsonTasks {
let task = Task()
task.fillNew(jsonTask)
self.tasks.append(task);
}
self.tasks = Task.sortGenericTasks(self.tasks)
dispatch_async(dispatch_get_main_queue()) {
let realm = try! Realm()
try! realm.write {
realm.add(self.tasks, update: true)
}
}
}
}
I'm currently using the latest version of realm, but can not pinpoint exactly what is happening or if I'm doing something wrong.

Converting PFObject (Parse) into JSON in Swift?

Is there a way to convert a PFObject from Parse into JSON? I saved as JSON, but when I try to load I'm getting [AnyObject] back. Casting to JSON won't work:
class func loadPeople() -> [String : Person] {
var peopleDictionary: [String : Person] = [:]
let query = PFQuery(className: "userPeeps")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
//this only returns the first entry, how do I get them all?
if let peopleFromParse = objects?.first?.objectForKey("userPeeps") as? JSON {
for name in peopleFromParse.keys {
if let personJSON = peopleFromParse[name] as? JSON,
let person = Person(json: personJSON) {
peopleDictionary[name] = person
}
}
}
below is my save function, which works and saves the JSON into Parse like I want:
class DataManager {
typealias JSON = [String: AnyObject]
class func savePeople(people: [String : Person]) {
var peopleDictionary = people
var peopleJSON: JSON = [:]
for name in peopleDictionary.keys {
peopleJSON[name] = peopleDictionary[name]!.toJSON()
}
let userPeeps = PFObject(className: "userPeeps")
userPeeps.setObject(peopleJSON, forKey: "userPeeps")
userPeeps.saveInBackgroundWithBlock { (succeeded, error) -> Void in
if succeeded {
println("Object Uploaded")
} else {
println("Error: \(error) \(error!.userInfo!)")
}
}
}
So the answer (as Paulw11 points out above) is that "objects" is sort of a wrapper for the real data, so it was necessary to iterate through the array and store each value as JSON:
var peopleDictionary: [String : Person] = [:]
//1 load the dictionary of JSON for key people from Parse
let query = PFQuery(className: "userPeeps")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
if let unwrappedObjects = objects {
for object in unwrappedObjects {
if let peopleFromParse = object as? JSON {
for name in peopleFromParse.keys {
if let personJSON = peopleFromParse[name] as? JSON,
let person = Person(json: personJSON) {
peopleDictionary[name] = person
}
}
}
}
}

Resources