Parsing JSONArray with Swift - ios

How can I parse that json array ,which is string format with using Swift ? I was doing by hand like removing brackets and splitting text by commas etc. But algorithm goes wrong if any of array elements has comma inside like ("DEF1,23") . So is there any predefined library from Apple to do that ?
Note : I saw some solutions in stackoverflow but that functions takes inputs which is NSData formatted so, I need string implementation of that parsing functions.
var jsonstring : String = "["ABC123","DEF1,23","ASD54,21"]"

let str: NSString = /* json string */
var error: NSError?
if let data = NSJSONSerialization.JSONObjectWithData(str.dataUsingEncoding(NSUTF8StringEncoding)!, options: nil, error: &error) as? NSArray {
println(data)
}

Related

Convert plain string to JSON and back in Swift

Let's say there's a JSON-string I got from server: "\"3\"" (with quotes, i.e. length == 3 here)
In Android-world, I can do:
gson.fromJson(json, new TypeToken<String>() {}.getType()); - it returns "3" (i.e. length == 1)
In C#-world, can use NewtonSoft.Json:
JsonConvert.DeserializeObject<string>(json, settings) - it returns "3" (i.e. length == 1)
And other way around, I do have string I want to serialize as a JSON.
In Android I'd do gson.toJson("\"3\"") and in C# - JsonConvert.SerializeObject("\"3\"")
The problem with JSONSerialization is that it doesn't treat plain string as a valid JSON: JSONSerialization.isValidJSONObject("\"3\"") == *false*
What would be equivalent in Swift / Obj-C world?
The ugly workaround I've found (except of just adding/removing quotes) so far is to wrap string into 1-item-array to make JSONSerialization happy and then remove "[","]" from resulted JSON-string (and other way around - add "[", "]" before deserialization), but it's a way too disgusting to be the real solution for this problem.
When de-serializing JSON which does not have an array or
dictionary as top-level object you can pass the
.allowFragments option:
let jsonString = "\"3\""
let jsonData = jsonString.data(using: .utf8)!
let json = try! JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
if let str = json as? String {
print(str) // 3
}
However, there seems to be no way to serialize a plain string to JSON
with the JSONSerialization class from the Foundation library.
Note that according to the JSON specification,
a JSON object is a collection of name/value pairs (dictionary) or
an ordered list of values (array). A single string is not a valid
JSON object.

How to convert a string into JSON using SwiftyJSON

The string to convert:
[{"description": "Hi","id":2,"img":"hi.png"},{"description": "pet","id":10,"img":"pet.png"},{"description": "Hello! :D","id":12,"img":"hello.png"}]
The code to convert the string:
var json = JSON(stringLiteral: stringJSON)
The string is converted to JSON and when I try to count how many blocks are inside this JSON (expected answer = 3), I get 0.
print(json.count)
Console Output: 0
What am I missing? Help is very appreciated.
Actually, there was a built-in function in SwifyJSON called parse
/**
Create a JSON from JSON string
- parameter string: Normal json string like '{"a":"b"}'
- returns: The created JSON
*/
public static func parse(string:String) -> JSON {
return string.dataUsingEncoding(NSUTF8StringEncoding)
.flatMap({JSON(data: $0)}) ?? JSON(NSNull())
}
Note that
var json = JSON.parse(stringJSON)
its now changed to
var json = JSON.init(parseJSON:stringJSON)
I fix it on this way.
I will use the variable "string" as the variable what contains the JSON.
1.
encode the sting with NSData like this
var encodedString : NSData = (string as NSString).dataUsingEncoding(NSUTF8StringEncoding)!
un-encode the string encoded (this may be sound a little bit weird hehehe):
var finalJSON = JSON(data: encodedString)
Then you can do whatever you like with this JSON.
Like get the number of sections in it (this was the real question) with
finalJSON.count or print(finalJSON[0]) or whatever you like to do.
There is a built-in parser in SwiftyJSON:
let json = JSON.init(parseJSON: responseString)
Don't forget to import SwiftyJSON!
I'm using as follows:
let yourString = NSMutableString()
let dataToConvert = yourString.data(using: String.Encoding.utf8.rawValue)
let json = JSON(data: dataToConvert!)
print("\nYour string: " + String(describing: json))
Swift4
let json = string.data(using: String.Encoding.utf8).flatMap({try? JSON(data: $0)}) ?? JSON(NSNull())

iOS 9 JSON Parsing loop

I'm creating an app that should retrieve some JSON from a database.
This is how my JSON looks:
[{"id":"1","longitude":"10","latitude":"10","visibility":"5","timestampAdded":"2015-10-01 15:01:39"},{"id":"2","longitude":"15","latitude":"15","visibility":"5","timestampAdded":"2015-10-01 15:06:25"}]
And this is the code i use:
if let jsonResult = JSON as? Array<Dictionary<String,String>> {
let longitudeValue = jsonResult[0]["longitude"]
let latitudeValue = jsonResult[0]["latitude"]
let visibilityValue = jsonResult[0]["visibility"]
print(longitudeValue!)
print(latitudeValue!)
print(visibilityValue!)
}
As you can see it only gets the first chunk from the JSON and if there are no JSON at all it will crash, but if i want it to count the amount and make an array out of it like this:
var longitudeArray = [10, 15]
var latitudeArray = [10, 15]
And so on...
I also need this to be apple watch compatible so i can't use SwiftyJSON.
What do i do? I really hope you can help me!
Thanks.
SOLVED!
Problems was solved by "Eric D."
This is the code:
do {
if let url = NSURL(string: "YOU URL HERE"),
let data = NSData(contentsOfURL: url),
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [[String:AnyObject]] {
print(jsonResult)
let longitudeArray = jsonResult.flatMap { $0["longitude"] as? String }
let latitudeArray = jsonResult.flatMap { $0["latitude"] as? String }
print(longitudeArray)
print(latitudeArray)
}
} catch let error as NSError {
print(error.description)
}
Thank you soo much Eric!! :-)
You could use flatMap to get an array of your elements:
let longitudeArray = jsonResult.flatMap { $0["longitude"] as? String }
let latitudeArray = jsonResult.flatMap { $0["latitude"] as? String }
etc.
flatMap is like map but unwraps optionals, which is adequate because we need to safely cast the type of the object we get from each dictionary in the json array.
$0 represents the object in the current iteration of flatMap of the array it's applied to.
If you're currently using SwiftyJSON, then that would be:
let longitudeArray = jsonResult.flatMap { $1["longitude"].string }
let latitudeArray = jsonResult.flatMap { $1["latitude"].string }
because .string is SwiftyJSON's optional String value getter.
But as you said, you don't want to use it (anymore), so you need to use NSJSONSerialization to decode your JSON data, there's plenty of examples on the Web and on SO. Then you will be able to use my original answer.
You're already getting an array with all of the elements (not just the first one. you're simply only accessing the first one). jsonResult is an array of dictionaries. Each dictionary (in this case, based on the json you provided) contains these elements: id, longitude, latitude, visibility and timestampAdded. In order to access each of them, you can simply loop over jsonResult and access the i'th element (and not always the 0 element). This will also prevent the crash you're experiencing with the json is blank or invalid (since you'll only be going over the valid elements in jsonResult.
This will give you the flexibility to create the custom arrays you wish to create (in order to create an array of all of the longitudes, for example, you will simply add that element to the new array while looping over jsonResult). However, if you'd like to save yourself the trouble of manually building these arrays and assuming you have control over the json structure, I would recommend changing the received json to the relevant structure (a dictionary or arrays instead of an array of dictionaries), so it would better fit your needs and provide you the results in the relevant format right "out of the box".

iOS, Swift - JSON Response. VK SDK

I am using the VK SDK for iOS, and I need to take out the images URLs from JSON response from VK.
The response is look like this:
The actual response could be AnyObject OR String, and I only need the largest image URL (photo_1280) as string.
In the response comes 1-10 photos and which parameters I will need to change to get the particular photo like first or second and so on.
I'm using Swift in my project but can understand Objective-C.
JSON is just a format that lets you exchange information between languages (or objects).
You need to 'parse' the string into a JSON object. Its a little different for each language. For example on iOS I create my UI elements from a JSON file where I load the file and create a dictionary object from it. In your case you are doing it from a string. You need to turn that string into a valid object in your language. In Swift I prefer a Dictionary. So I use a typealias for that and cast the nsDictionary as that type of object.
Then to access objects, I access keys in the dictionary. In your case you would create a dictionary object too, and access the "attachments" object, which in your case is an array so you'll need to do extra processing on it to get each image (i.e. go through the array). This should get you going.
typealias Dict = Dictionary<String,AnyObject>
func loadDictionaryFromJSON(jsonString:String) -> Dict
{
var JSONData:NSData! = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
var JSONError:NSError?
let swiftObject:AnyObject = NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions.AllowFragments, error: &JSONError)!
if let nsDictionaryObject = swiftObject as? NSDictionary
{
if let dictionaryObject = nsDictionaryObject as Dictionary?
{
return dictionaryObject as Dict
}else
{
println("Error could not make dictionary from NSDictionary in \(self)")
}
}else
{
"Error could not make NSDictionary in \(self)"
}
println("Empty dictionary passed, fix it!")
return Dict()
}
Now to access things you just do
var objects:Dictionary<String,AnyObject> = loadDictionaryFromJSON("{"what":"ever"}")
var whatever = objects["what"]

Cannot downcast from 'AnyObject' whilst parsing JSON array

I'm in the process of trying to migrate an existing project to Swift, its more of a learning exercise but I think I've hit a problem which seems pretty basic and I think it may relate to my lack of understanding of AnyObject.
I've created a object that consists of a number of variables and is initialised as:
var customObject:MycustomObject = MYcustomObject()
I'm then using NSURLConnection to retrieve JSON data and this all appears to be working correctly as the jsonArray is being populated with data
jsonArray = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSArray
I'm then looping through the array and attempting to parse the data and this is where I'm experiencing problems, the for loop defaults details to AnyObject.
for details:AnyObject in jsonArray {
parseDetail(details as NSDictionary)
}
The problem relates to the integerValue, its generating an "Cannot downcast from 'AnyObject' to non-#objc...." error
func parseDetail(element: NSDictionary ) {
self.customObject.stringValue = element["id"] as String
self.customObject.integerValue = element["type"] as Integer
}
There also seems to be some differences in the way NSString & String are working, my understanding so far is that when using Swift I should be using the native types Sting, Float Integer etc etc. If I use NSString the stringValue displays correctly in the debug window but when using the type String I receive the following:
{
core = {
_baseAddress = Builtin.RawPointer = 0x0b227f49
_countAndFlags = 1073741828
_owner = Some {
Some = (instance_type = Builtin.RawPointer = 0x0b227f40 -> 0x006d58f0 (void *)0x006d58c8: __NSCFString)
}
}
}
Sorry this is a little long but is there any info regarding threading, this is my next challenge?
NSThread.detachNewThreadSelector(Selector: parseDetail(), toTarget: self, withObject: nil)
Here's a little example that does the job. First, here's a model class for our purposes:
class CustomObject {
var name: String = ""
var age : Int = 0
init(json: Dictionary<String, AnyObject>) {
name = json["name"] as NSString
age = (json["age" ] as NSNumber).integerValue
}
}
As you can see, we still need to deal with NSNumbers and NSStrings since NSJSONSerialization is not yet updated to the Swift types.
Next, a little json.txt file residing in our project:
[{"name":"Alice","age":30},{"name":"Bob","age":40}]
And last, the parser, right in application launch for a quick test:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
var filePath = NSBundle.mainBundle().pathForResource("json", ofType:"txt")
var data = NSData(contentsOfFile:filePath)
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as Array<Dictionary<String, AnyObject>>
var customObjects = json.map { dict in CustomObject(json: dict) }
println(customObjects[0].name)
println(customObjects[0].age)
return true
}
This will print:
Alice
30
A progress update, the following items are correct and are working:
.interegervalue
.doublevalue
.boolvalue
The reason why the above were appearing not to be working is that the JSON data had changed and some elements no longer existed. So element["type"] didn't exist, using Swift this results in a crash whereas in Objective-C no crash occurs.
All part of the learning process..

Resources