Pointer to inner nodes of JSON (NSDictionary) - ios

{
"outterList": {
"section1": {
"entry1": {
"value": ""
},
"entry2": {
"value": ""
},
"entry3": {
"value": ""
},
"innerSection": {
"entry1": {
"value": ""
},
"entry2": {
"value": ""
}
}
},
"section2": {
"entry1": {
"value": ""
}
}
}
}
Problem statement is to read the above json and store it back in the same format with the "value" fields updated as per the local logic.
I initially go and parse the above NSDictionary and convert it to an NSArray (Mutable), which holds all the "entry" nodes in a custom holder class.
Is it possible that my NSArray's holder object can store a direct pointer to "outterList" -> "section1" -> "innerSection" -> "entry2", so that whenever I get a new value from my logic and I have to update it, I can do that immediately on the fly and not parsing it again to reach to that node.

Value in a dictionary will be changed if you modify the object itself:
myObj.value = newValue;
but it won't change if you simply assign pointer to another object:
myObj = [MyObj new];
so it won't work with JSON strings or numbers.
Also you can access dictionary values faster using valueForKeyPath like so:
[dict setValue:newValue forKeyPath:#"outterList.section1.entry1.value"]

Related

Configuring collectionView:cellForRowAt based on an array of strings

I've got this JSON data (not verbatim) that I get from the backend. It contains the actual data and an array of strings describing the sequence of cells to be shown:
{
"data": [
{
"name": "text",
"data": {
"text": "some text"
}
},
{
"name": "pic",
"data": {
"url": "https://somepic.jpg",
"text": "picture"
}
},
{
"name": "switcher",
"data": {
"id": 1,
"options": [
{
"id": 0,
"text": "option 1"
},
{
"id": 1,
"text": "option 2"
},
{
"id": 2,
"text": "option 3"
}
]
}
}
],
"view": [
"text",
"pic",
"switcher",
"text"
]
}
The problem is that I can't get my head around how to configure cellForRowAt: and get the right order of cells in one section. (i.e. text, pic, selector, text).
I tried a couple of things:
Looping through "view" array and switching on each individual view string to dequeue a specific cell but that doesn't seem to work since returning a cell from a switch case gives a "unexpected non-void return value in void function" error.
I was also thinking about turning a "view" array into a dictionary and then, based on keys in it, dequeue a specific cell but then again, a dictionary should have unique keys meaning that I will not have 2 "text" entries, one of them will be lost.
So, the questions is: how can I dequeue specific cells based on the array of strings? It's also important to understand that it should be done in one section. I'm feeling that it's somehow not that difficult to implement but I'm kinda lost right now. Thanks!
you need to transform your view list and data array into an array of cell contents that you can use inside the TableViewDelegate and TableViewSource method :
var cellsContents : [Int] = []
for aView in view {
var found = false
var index = 0
for aData in data {
if !found {
if let name = aData["name"] as? String {
if aView == name {
found = true
cellsContents.append(index)
continue
}
}
index = index + 1
}
}
}
Then :
number of rows : cellsContents.count
type and contents for a row : data[cellsContents[indexPath.row]]["name"] and data[cellsContents[indexPath.row]]["data"]

Retrieve JSON Object and populate array Swift

I have a json object of this structure:
[
{
"id": 1,
"seat_no": 6
},
{
"id": 2,
"seat_no": 27
}
]
The main challenge is that I need to get the seat_no and add that to an int array which I will be using later on:
func getReserved() -> [Int] {
var reservedSeatsJSON : JSON = JSON()
var seats = Int()
var reservedSeats = [Int]()
for item in reservedSeatsJSON.array! {
seats = item["seat_no"].int!
reservedSeats.append(seats)
self.reservedSeatsLabel.text = "Reserved(\(reservedSeatsJSON.array!.count))"
}
return reservedSeats
}
Each time I run this, the reservedSeats returns empty. The main idea here is that I need to populate an int array in a for loop and return the populated array outside the for loop
First check is reservedSeatsJSON json contains actual JSON?
if it contains actual JSON then do as like below. short and simple way.
func getReserved() -> [Int] {
var reservedSeatsJSON : JSON = JSON()
self.reservedSeatsLabel.text = "Reserved(\(reservedSeatsJSON.array.count))"
return reservedSeatsJSON.arrayValue.map { $0["seat_no"].intValue }
}

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.

json parsing in swift

Here is my Json
{
"id": "63",
"name": "Magnet",
"price": "₹1250",
"description": "",
"image": [
"catalog/IMG-20150119-WA0012_azw1e3ge.jpg",
"catalog/IMG-20150119-WA0029_6mr3ndda.jpg",
"catalog/IMG-20150119-WA0028_ooc2ea52.jpg",
"catalog/IMG-20150119-WA0026_4wjz5882.jpg",
"catalog/IMG-20150119-WA0024_e38xvczi.jpg",
"catalog/IMG-20150119-WA0020_vyzhfkvf.jpg",
"catalog/IMG-20150119-WA0018_u686bmde.jpg",
"catalog/IMG-20150119-WA0016_c8ffp19i.jpg"
],
"thumb_image": [
"cache/catalog/IMG-20150119-WA0012_azw1e3ge-300x412.jpg",
"cache/catalog/IMG-20150119-WA0029_6mr3ndda-300x412.jpg",
"cache/catalog/IMG-20150119-WA0028_ooc2ea52-300x412.jpg",
"cache/catalog/IMG-20150119-WA0026_4wjz5882-300x412.jpg",
"cache/catalog/IMG-20150119-WA0024_e38xvczi-300x412.jpg",
"cache/catalog/IMG-20150119-WA0020_vyzhfkvf-300x412.jpg",
"cache/catalog/IMG-20150119-WA0018_u686bmde-300x412.jpg",
"cache/catalog/IMG-20150119-WA0016_c8ffp19i-300x412.jpg"
],
"specifications": [
{
"Fabrics": [
"Pure chiffon straight cut suits 48" length"
]
},
{
"MOQ": [
"Minimum 10"
]
}
]
}
In above json string the "specification" arraylist has dynamic number of key and each key has dynamic number of values
So how can parse this? Please help if anyone knows this...
Thanks in advance
There are multiple ways to do parsing. In your case specifications should be an array, so you'll be able to loop on each items.
You might want to :
Create your own JSON parse class / methods ;
Use an existing library to parse JSON.
For the second option, you can give a look at the following :
https://github.com/Wolg/awesome-swift#jsonxml-manipulation
var yourJson = data as? NSDictionary
if let id = yourJson.valueForKey("id") as String
{
//save your id from json
}
if let name = yourJson.valueForKey("name") as String
{
//save your name
}
...
if let images = yourJson.valueForKey("image") as NSArray
{
for im in images
{
//save image
}
//the same for all othe images
}
... And so on...
You should also watch some tutorials, to understand the basics of JSON parsing..
https://www.youtube.com/watch?v=MtcscjMxxq4

Swift. Mapping from JSON to object model. Array of Array

There's model in JSON format
{
"offline": false,
"data": {
"path": [
[ {
"Latitude": 56.789351316653,
"Longitude": 60.6053340947616
}, {
"Latitude": 56.78674,
"Longitude": 60.60613
}
], [ {
"Latitude": 56.79071,
"Longitude": 60.60492
}, {
"Latitude": 56.79129,
"Longitude": 60.60493
} ]
] } }
and object model on swift
http://pastebin.com/j0mK8eYG
Issue: can't parse the path field of json, because there is an array of arrays.
In the case of arrays with single dimension all works well.
I had to do something similar. I looked at the implementation of mtl_JSONArrayTransformerWithModelClass which transforms an array of dictionaries to and from an array of MTLModels. So I made a similar transformer which expects an array of array of dictionaries/MTLModels. I iterate over the outermost array and transform each array of dictionaries with the mtl_JSONArrayTransformerWithModelClass.
+ (NSValueTransformer *)JSONArrayOfArraysTransformerWithModelClass:(Class)modelClass
NSValueTransformer *arrayTransformer = [NSValueTransformer mtl_JSONArrayTransformerWithModelClass:modelClass];
return [MTLValueTransformer
reversibleTransformerWithForwardBlock:^id(NSArray *arrays) {
if (arrays == nil) return nil;
NSAssert([arrays isKindOfClass:NSArray.class], #"Expected an array, got: %#", arrays);
NSMutableArray *modelArrays = [NSMutableArray arrayWithCapacity:[arrays count]];
for (id JSONArray in arrays) {
if (JSONArray == NSNull.null) {
[modelArrays addObject:JSONArray];
continue;
}
NSAssert([JSONArray isKindOfClass:NSArray.class], #"Expected an array of arrays of dictionaries, got array of: %#", JSONArray);
NSArray *modelArray = [arrayTransformer transformedValue:JSONArray];
if (modelArray == nil) continue;
[modelArrays addObject:modelArray];
}
return modelArrays;
}
reverseBlock:^id(NSArray *arrays) {
if (arrays == nil) return nil;
NSAssert([arrays isKindOfClass:NSArray.class], #"Expected an array of arrays of MTLModels, got: %#", arrays);
NSMutableArray *modelArrays = [NSMutableArray arrayWithCapacity:modelArrays.count];
for (id modelArray in arrays) {
if (modelArray == NSNull.null) {
[modelArrays addObject:NSNull.null];
continue;
}
NSAssert([modelArray isKindOfClass:NSArray.class], #"Expected an array of arrays, got array of: %#", modelArray);
NSArray *array = [arrayTransformer reverseTransformedValue:modelArray];
if (array == nil) continue;
[modelArrays addObject:array];
}
return modelArrays;
}];
}
1 - Generate the Swift models by copying the same Json at http://www.json4swift.com
2 - Copy the generated files to your project
3 - Open Data.swift, replace the following method
required public init?(dictionary: NSDictionary) {
if (dictionary["path"] != nil) { path = Path.modelsFromDictionaryArray(dictionary["path"] as! NSArray) }
}
to
required public init?(dictionary: NSDictionary) {
if let paths = dictionary["path"] as? NSArray {
let allPaths = NSMutableArray()
for patharr in paths {
allPaths.addObjectsFromArray(Path.modelsFromDictionaryArray(patharr as! NSArray))
}
}
}
4 - Success!
If you want to use a third party : There is this awesome and cleanly implemented open source objectMapper, works with Alamofire as well.
Create models implementing protocol "Mappable" and custom arrays will be handled according to the Class specified.
Reduces boiler plate code like anything.
If you want to parse it on your own : Simply create a helper method in your mdoel class to parse it using for loop.
init (responseDict: [String: Any]) {
var itemModels : [ItemClass] = []
let itemDictArray = responseDict["items"] as! [[String:Any]]
for itemDict in itemDictArray {
itemModels.append(ItemClass.init(dict: (itemDict as [String:Any]) ))
}
}

Resources