How to insert and delete objects in json file swift - ios

So I have this json which has an array of some objects.
[
{
"id": 1,
"title": "First Object"
},
{
"id": 2,
"title": "Second Object"
},
{
"id": 3,
"title": "Third Object"
}
]
I'm parsing the json with the following code,
struct MyModel: Codable {
let id: Int?
let title: String?
}
var myModel = [MyModel]()
func decodeData(url: URL) {
do {
let jsonData = try Data(contentsOf: url)
let decoder = JSONDecoder()
myModel = try decoder.decode([MyModel].self, from: jsonData)
} catch let jsonError {
print("Error serializing json", jsonError)
}
}
Everything works fine when it comes to reading the json. What I can seem to figure out is how to delete and insert object into the same json file.
For example, delete object with "id"=2, or insert a new object after object with "id"=3 or between objects with "id"=1 and "id"=2.
What I was thinking was to read the entire json file into an array. Then modify the data in the array by deleting and appending elements in the array then overwriting the json file with everything in the modified array.
For some reason this approach doesn't seem practical. Might be fine for a small numbers of objects but what happens if/when the number of objects reach a number in the 100+ range.
Am I taking the right approach by reading the contents of the json file into an array then modifying and overwriting the json file with the contents of the modify array or is there a proper way of achieving this?

You can delete any object from Dictionary using this piece of code, convert json to Dictionary.
if let index = values.index(forKey: id) {
values.remove(at: index)
}
and in the same way you can get the index of particular object where you want to insert new object.

Related

Decode JSON array inside a JSON dictionary without creating Boilerplate Types

The JSON:
let jsonString = """
{
"groups": [
{
"id": "oruoiru",
"testProp": "rhorir",
"name": "* C-Level",
"description": "C-Level"
},
{
"id": "seese",
"testProp": "seses",
"name": "CDLevel",
"description": "CDLevel"
}
],
"totalCount": 41
}
"""
Type:
struct Group: Codable {
var id: String
var name: String
}
I would like to decode this JSON to only output an array of Group type without having to create boilerplate type like:
struct GroupsResponse: Codable {
var groups: [Group]
var totalCount: Int
}
and use:
let data = jsonString.data(using: .utf8)
let decoded = try! JSONDecoder().decode([Group].self, from: data!)
I tried getting the containers from inside the initialiser of the Group type, but the program already crashes outside at the decoding line with Swift.DecodingError.typeMismatch error
One solution that does work is doing something like:
let topLevel = try! JSONSerialization.jsonObject(with: data) as? [String: Any]
let groupsNode = topLevel?["Groups"] as? [[String: Any]]
let groups = try! JSONSerialization.data(withJSONObject: groupsNode!)
let decoded = try! JSONDecoder().decode([Group].self, from: groups)
but this seems very hacky. Is there an elegant way to handle this?
You cannot avoid the top level response struct using JSONDecoder. There has to be a type for it to work on. And you can't use Dictionary as the top level object (ie [String: [Group]]), since there's a totalCount field that doesn't have an array of Group. All the comments are correct. Just write the little wrapper. It's one line long:
struct GroupsResponse: Codable { var groups: [Group] }
There's no need to decode fields you don't care about.
But you said "for education," and it's all code, so of course you can replace JSONDecoder with something that can do this. You tried to do that with NSJSONSerialization, but that's extremely clunky. You can also just write your own version of JSONDecoder, and then do it like this:
let decoder = RNJSONDecoder()
let response = try decoder.decode(JSON.self, from: json)
let groups = try decoder.decode([Group].self, from: response.groups)
This avoids any re-encoding (RNJSONDecoder can decode directly from a JSON data structure; it doesn't have to convert it back to Data first). It also requires about 2600 lines of incredibly tedious boilerplate code, mostly copy and pasted out of stdlib. Implementing your own Decoder implementation is obnoxious.
If you wanted to get fancier, you could scan the data for the section that corresponds to the property you want, and decode just that part. While implementing the Decoder protocol is very hard, parsing JSON is quite straight-forward. I'm currently doing a bunch of JSON experiments, so I may try writing that and I'll update this if I do.
But the answers are: "just write the tiny, simple, fast, easy to understand response wrapper," or "replace JSONDecoder with something more powerful."
UPDATE:
I went ahead and built the scanner I mentioned, just to show how it could work. It's still a bit rough, but it allows things like:
let scanner = JSONScanner()
let groupJSON = try scanner.extractData(from: Data(jsonString.utf8),
forPath: ["groups", 1])
let group = try JSONDecoder().decode(Group.self, from: groupJSON)
XCTAssertEqual(group.id, "seese")
So you can just extract the part of the data you want to parse, and not worry about parsing the rest.

How to model data from JSON array with no property name into swift so It can be parsed? SWIFT

Im stuck trying to model a JSON array which has no property name into my swift project with the goal of parsing the data and using it in my app. I know how to do this when there is a NAME for the array but I don't know how to make swift and this lackluster JSON understand each other. The path to the first "Company" in the JSON is "0.Company". The error I get is "Value of type 'WorkData' has no member '0'"
Im including pictures of my full project so it is easier to understand the structure of the code and what im trying to do. Please look at the picture for a clearer understanding I apologize if Im not explaining it well i'm new to programming.
import Foundation
class WorkData: Codable {
let WorkData: [WorkData]
let Company: String
let worklogDate: String
let issue: String
}
func parseData(jsonDataInput: Data) {
let decoder = JSONDecoder() // an object that decodes JSON data
do {
let decodedData = try decoder.decode(WorkData.self, from: jsonDataInput)
let Company = decodedData.0.Company
let worklogDate = decodedData.0.worklogDate
let issue = decodedData.0.issue
} catch {
print (error)
}
}
}
json
Trying to model JSON in Swift
Parsing JSON
You cannot start JSON with an array because JSON itself is an object {}
See example below:
{
"WorkData" : [
{"Company" : ""},
{"Company" : ""},
{"Company" : ""}
]
}
let decodedData = try decoder.decode(LocalizationsResponse.self, from: jsonDataInput)
decodedData will be an array

Can't retrieve the info after downloading this data

I'm trying to access the data downloaded from a website which looks like this:
{"quizzes":[
{"id":1, "question": "Can't solve this :("},
{"id":2, "question": "Someone help pls"}]}
The data downloaded looks way more complex, with more values, more keys, and some keys being associated to another dictionary of Key:String, but since I can't even access the most simple fields I thought I would start with this.
I'm using JSONSerialization, so my question is:
If I want to create a variable where i can save the downloaded data... Which would be it's type? I would say [String:[String:Any]], but I'm not sure if "quizzes" represents a key on this specific key, since the data starts with '{' and not with '['.
Instead of using JSONSerialization one could use JSONDecoder.
Example
struct Question: Decodable {
let id: Int
let question: String
}
struct Quiz: Decodable {
let quizzes: [Question]
}
Assuming jsonStr is a string with the JSON in your question:
if let jsonData = jsonStr.data(using: .utf8) {
if let quiz = try? JSONDecoder().decode(Quiz.self, from: jsonData) {
for question in quiz.quizzes {
print("\(question.id) \"\(question.question)\"")
}
}
}
Output
1 "Can't solve this :("
2 "Someone help pls"

How to read-write json file without disturbing the sequences of its key-value pairs in iOS?

I am serializing a json file whose key-value pairs should not be shuffled when writing to a new file after editing. Even if I do not edit it still goes shuffles the pairs.
I just need the same sequence of the key-value pairs in the new file(written file) as it was in the previous file that I read.
Here is the sample json
[
{
"StudentName":"Amit",
"StudentId":"1"
},
{
"StudentName":"Lalit",
"StudentId":"2"
},
{
"StudentName":"Ram",
"StudentId":"3"
},
{
"StudentName":"Shyam",
"StudentId":"4"
}
]
What I am getting after writing the jsonObject to a new file is:
[
{
"StudentName":"Lalit",
"StudentId":"2"
},
{
"StudentName":"Ram",
"StudentId":"3"
},
{
"StudentName":"Shyam",
"StudentId":"4"
},
{
"StudentName":"Amit",
"StudentId":"1"
}
]
I have two code files, since I am working on command line tool.
1)main.swift
import Foundation
var behavioralJsonObject : AnyObject
var newBehavioralDataObject: NSData = NSData()
let fileManager = NSFileManager.defaultManager()
var path = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first
var stringPath: String = (path?.path)!
var behavioralFilePath: String = stringPath.stringByAppendingString("/BehavioralFile.json")
var newBehavioralFilePath : String = stringPath.stringByAppendingString("/BehavioralFileNew.json")
behavioralJsonObject = MyJsonParser().jsonObject(withFilePath: behavioralFilePath)
print(behavioralJsonObject.description)
newBehavioralDataObject = try NSJSONSerialization.dataWithJSONObject(behavioralJsonObject, options: .PrettyPrinted)
newBehavioralDataObject.writeToFile(newBehavioralFilePath, atomically: true)
2) MyJsonParser.swift
import Foundation
class MyJsonParser: NSObject {
func jsonObject(withFilePath path:String)-> AnyObject{
let dataObject = NSData.init(contentsOfFile: path)
var jsonObject: AnyObject = []
do{
jsonObject = try NSJSONSerialization.JSONObjectWithData(dataObject!, options: .AllowFragments)
}
catch{
print("Serialization error : \(error)")
}
return jsonObject;
}
}
Has anybody already faced this problem or has a solution for this?
Please re-check your code. If your code is correct, you are parsing a JSON array, converting it back to data, and don't write the array elements in the correct order. If that is what really happens, then the only thing you can do is send a bug report to Apple and let them fix it. Array elements have a defined order; if that is changed by the act of reading and writing the array then there is a serious bug somewhere.
It's different with dictionaries. The key/value pairs in a dictionary are unordered. You can't find out in which order they were parsed, and the order in which they are written is undefined. So if your first array element was written as
{
"StudentId":"1"
"StudentName":"Amit",
},
that would be totally to be expected and completely correct. Changing the order of array elements however is a bug. Note that if you actually read a dictionary and printed out the array of values of the dictionary, that would again be in undefined order.

parse JSON Array from GET request Alamofire Swift 2

I'm new to Swift, my task is to get data from GET request and present its data on UI. Below is my code:
let credentialData = "\(user):\(password)".dataUsingEncoding(NSUTF8StringEncoding)!
let base64Credentials = credentialData.base64EncodedStringWithOptions([])
let headers = ["Authorization": "Basic \(base64Credentials)"]
Alamofire.request(.GET, myUrl, headers: headers)
.responseJSON{ JSON in
if let jsonResult = JSON as? Array<Dictionary<String, String>> {
let title = jsonResult[0]["title"]
print(title)
}
}
I'm able to get data with request but I don't know how to parse JSON object in some format (probably json array) that can be later used to present in TableView. Please help
Data example:
[
{
"title": "Sony",
"content": "Tech content",
"image": "http://google.com/content/device.jpg?06"
},
{
"title": "Nexus",
"content": "Nexus 6 is a new beginning",
"image": "http://google.com/content/device.jpg?01"
} ]
JSON data can be represented in different forms. It can be encoded as a string or converted into known data types on the platform. The main components of json are arrays, associative arrays (or dictionaries), and values.
The swift struct you are displaying reads as follows.
It's an array. Array content displayed here starts and ends with [] like [1,2,3] would be an array of ints.
The data in the the array a list of dictionaries. dictionary start and ends with {} . like {"key":"value"}
Those dictionaries contain the keys "title","content" and "image".
because you requested responseJSON from alamo file you will be returned a parsed structure and all you need to do read it like you would normal arrays and dictionaries as that is what it is.
You should read this documentation on how to make safe code that uses the above logic.
http://www.raywenderlich.com/82706/working-with-json-in-swift-tutorial

Resources