Alamofire + swiftyJSON array parsing - swift

When I got JSON {"categories": "[\"a/b/c\",\"d\"]"} from server,
Then How can I parse it to ["a/b/c", "d"]
When I using .arrayValue, then it always return []

You have an array represented as raw JSON string within your JSON. You have three options:
Try to manually scan through replacing occurrences of \" (which you'll have to confusingly escape both the backslash character and the quotation mark encountered with a backslash when you write your code, e.g. "\\\"") before you parse the JSON. To do this, you'd probably use responseData rather than responseJSON, do your manual replacement of characters, and then manually parse it yourself with NSJSONSerialization. The downside here is that you might want to check for other escaped characters, too (e.g. what if the nested JSON was pretty printed, then you might have \n or even \t in there, too, which you'd want to convert as well).
Manually run JSON parsing again for each of those values that contain a JSON string in the responseObject. If you're stuck with the existing JSON, I think this is probably safest.
For example, if the raw json was really just:
{"categories": "[\"a/b/c\",\"d\"]"}
Then you could do something like:
Alamofire.request(request)
.responseJSON { response in
guard response.result.error == nil else {
print(response.result.error!)
return
}
do {
if let dictionary = response.result.value as? [String: String],
let categoryData = dictionary["categories"]?.dataUsingEncoding(NSUTF8StringEncoding),
let categories = try NSJSONSerialization.JSONObjectWithData(categoryData, options: []) as? [String]
{
let result = ["categories" : categories]
print(result)
}
} catch {
print(error)
}
}
Then the result would be:
["categories": ["a/b/c", "d"]]
Fix the web service that's generating this JSON so that it's not doing this silliness. This is probably the best solution, though it will take some detective work to figure out why/how the web service put JSON inside JSON. I know that you say you can't change the server side, but you really should escalate this to someone on the server team to fix this. It's silly to write client code to work around some mistake (either a mistake in the server code itself, or how the server was provided the data in the first place).

First up, the response you're getting from the server is a String and not Array. Hence you'll get empty array when you do .arrayValue . If you want to convert the string into an array, first you need to remove the "[" & "]" and then you need to do the componentsSeparatedByString with string as "," for the resultant string. You can use the following code snippet to do that:
let str = "[\"a/b/c\",\"d\"]"
let filteredString = String (str.characters.filter { $0 != "[" && $0 != "]"})
print(filteredString)
let filteredStringArray = filteredString.componentsSeparatedByString(",")
print(filteredStringArray)
HTH :)

Related

Retrieve child entry from Firebase Realtime Database without []. - Swift 5

I'm a little stuck with something small but that is giving me some headaches! I have a Realtime Database and I am able to retrieve the information I need from it. My only problem is that instead of printing for example (ex.: 200) is printing (ex.: [200])!
This is my code:
func readData() {
FirebaseDatabase.Database.database().reference().child("Available_Funds").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value as? [String: Any] else {
return
}
let amountWallet = value.values
print(amountWallet)
self.currentBalanceLabel.text = "$" + "\(amountWallet)"
print("\(value)")
})
}
Right now what I get printed with this code is $[200] for example, instead of just $200, which is what I intend to get.
Tried looking online, but no luck with this! Does someone know how to remove these square brackets from printing?
values is an Array -- thus the []. When you say value.values, you're asking for all of the values of the key/value pairs in snapshot.value.
If you intend to get a single value from it, you would use amountWallet[0] to get the first element. Keep in mind that this will crash if amountWallet has 0 elements (arrays are zero indexed).
amountWallet.first will give you an Optional that will be safe to use, but you would need to unwrap it for printing:
let amountWallet = value.values
if let singleAmount = amountWallet.first {
print(singleAmount)
self.currentBalanceLabel.text = "$" + "\(singleAmount)"
}
You're calling it back as an array of strings [String: Any]
You can either change this (remove []) or access the first element in the array: amountWallet[0].

json array parse return nil in swift 5

I have an json. In json there is multiple array. Here is my json...
["city": imrankhan136260#gmail.com,
"geofence_DEMRAhighway": {"type": "Polygon",
"coordinates":[[[90.45232332650568,23.714463300877014],[90.4532990923671,23.712264209703946],[90.45997933551394,23.714532021878767]]]}]
I have use bellow code get data ,it returns correct value
let city = myjson?["city"] as? String
print("city-->",city) // it returns imrankhan136260#gmail.com
But when it use bellow code get from geofence_DEMRAhighway key which returns nil. here is my code to get array
let geofence_BABUBAZARbridge = myjson?["geofence_BABUBAZARbridge"] as? [String: Any]
What is the wrong with the code. please help me to parse the json
This json is not filly correct.
It's starts from "[", but usualy {...} uses to map it to asociative array.
But if it works, you can still use it.
So, coordinates is not a string. You have to escape it, and it will see as
{\"type\": \"Polygon\",\r\n\"coordinates\":[[[90.45232332650568,23.714463300877014],[90.4532990923671,23.712264209703946],[90.45997933551394,23.714532021878767]]]}
If you can not change the format, then you shoud read it as JSonObject and convert it to string:
JSONSerialization.jsonObject(.....)
And then use
myjson?["city"]["type"]
You can try this:
JSONSerialization.data(withJSONObject: myjson?["city"], options: [])
myjson?["city"] will return an object, and JSONSerialization.data will convert it to string
And so one.

Syntax about try and "as?" "as!"

I read a snippet from this POST but have not quite understood.
https://www.hackingwithswift.com/example-code/system/how-to-parse-json-using-nsjsonserialization
I am confused on some syntax in the following snippets.
In the following, I am not knowing why try goes here, it is strange syntax to me. Any information about try and as! for this expression?
let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
In the following, I am not knowing what is as? [String] doing?
if let names = json["names"] as? [String] {
I know this question might be fundamental, I just need a keyword for me to search the related anwser, thanks. Here is my whole code block.
// define a string
let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"
// convert the string into NSUTF8StringEncoding
let data = str.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// put the following statements in try catch block
do {
// not knowing why try goes here, strange syntax to me.
// any higher conception about try and as! for this expression
let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
// json should be an object(dictionary, hash) use the key "names" to store string array into names
// not knowing what is `as? [String]` doing? any keyword for this syntax?
if let names = json["names"] as? [String] {
print(names)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
// not knowing why try goes here, strange syntax to me. what if it fails? as! is what syntax, any keyword to search for it?
The try here is to basically to try to perform the function on that line. If it fails it will go to the catch. Which in this case will just print("Failed to load: \(error.localizedDescription)")
// json should be an object(dictionary, hash) use the key "names" to store string array into names // not knowing what is as? [String] doing? any keyword for this syntax?
The line that you are confuse about is performing a if else on the json object. It check for a key that is name and also want to ensure that its value is a String therefore the as?. In this case if the key name does not exist it will not met the condition. If the value of name is not a String it will not met the condition.
Like what #Martin R mention in the comments, you should read up more on Type Casting in Swift. This should help you. Explanation with some example if you have trouble with Apple Documentation.
As for try catch it is actually use in other languages as well not just Swift. You can read up more here.

Swift NSData from base64encoded string returns nil

I'm communicating with a server in Swift retrieving image data. The incoming data is encoded as a base64 string. I am able to correctly receive and display the encoded strings. When I go to use the NSData class to decode the string back to binary data and display...
println(NSData(base64EncodedString: imageString, options: NSDataBase64DecodingOptions(0)))
The output is
nil
nil
nil
nil
nil
nil
One for each of the images received.
I've also tried
println(NSData(base64EncodedString: imageString, options: nil))
and the same results. Is there anything I am missing along the way?? I would put the image strings up but they are massively long...
For others that may be having this issue, make sure your Base64 encoded string has a length divisible by 4 (= should be used to pad the length).
See this StackOverflow answer here: https://stackoverflow.com/a/36366421/330494
Try to use IgnoreUnknownCharacters option.
Or try to use initWithBase64EncodedString from NSDataAdditions
This can also happen if the input is so-called "URL Safe" Base64 data. This data has the + symbol replaced by the - symbol, and the / symbol replaced by the _ symbol.
Fortunately it's straightforward to convert it:
inputString = [[inputString stringByReplacingOccurrencesOfString:#"-" withString:#"+"] stringByReplacingOccurrencesOfString:#"_" withString:#"/"];
A full list of variants is available on Wikipedia.
Based on Frank Schmitt's and Barlow Tucker's answers I've created an extension to Data to better handle base64 encoding:
extension Data {
static func decodeUrlSafeBase64(_ value: String) throws -> Data {
var stringtoDecode: String = value.replacingOccurrences(of: "-", with: "+")
stringtoDecode = stringtoDecode.replacingOccurrences(of: "_", with: "/")
switch (stringtoDecode.utf8.count % 4) {
case 2:
stringtoDecode += "=="
case 3:
stringtoDecode += "="
default:
break
}
guard let data = Data(base64Encoded: stringtoDecode, options: [.ignoreUnknownCharacters]) else {
throw NSError(domain: "decodeUrlSafeBase64", code: 1,
userInfo: [NSLocalizedDescriptionKey: "Can't decode base64 string"])
}
return data
}
}
so in your code, you can use it like this:
let baseEncodeText = "(.....)" //your base64 encoded string
let data = try Data.decodeUrlSafeBase64(baseEncodeText)

Any Method to remove comment characters from JSON response in Xcode?

I am new to JSON. Are there any methods in JSON parser to remove the comment characters from a response.
Eg. //{"response":"success".......
its SBJson for iPhone.
from http://code.google.com/p/json-framework
The JSON grammar doesn't allow comments. That doesn't answer your question obviously, but I suspect you'll have to do some string manipulation and replace all those comment characters with empty strings and parse it with the JSON library only after doing so.
Nowadays, very easy to do this:
Here's how you get and parse actual json
// normal json no comments
func getStuff() {
guard let url = URL(string: "http://you.com/x.json") else { return print("?") }
let configuration = URLSessionConfiguration.ephemeral
aSession = URLSession(configuration: configuration)
aSession.dataTask(with: url) { [weak self] data, response, error in
guard let _ = self else { return print("woe") }
guard let data = data else { return print("woe") }
do {
let result = try JSONDecoder().decode(YourStructure.self, from: data)
localBlah = Dictionary(uniqueKeysWithValues: result.whatever)
} catch let error {
print(error)
}
}.resume()
}
Here's how you get and parse "json" which has simple comment lines:
During development, remove #comment lines from "json":
Notice the line of code which decodes the data :
let result = try JSONDecoder().decode(YourStructure.self, from: data)
Simply paste in these three lines of code, before, that line:
let s = String(decoding: data, as: UTF8.self)
let fixed = s.replacingOccurrences(
of: "(?m)^#.*",
with: "",
options: .regularExpression)
guard let data2: Data = fixed.data(using: .utf8) else { return print("woe") }
let result = try JSONDecoder().decode(YourStructure.self, from: data2)
So during development in your "json" on your server, you can have things like ..
"measures": [{
"screen": "options",
"topMargin": 25,
#don 't change topmargin anyone
"leftMargin": 12,
#Note,
Steve prefers 13. But everyone
else prefers 12. "rightMargin": 20,
},
It's that simple.
Important note on using regex:
Regex is a sophisticated process. The example regex used in this post simply means
"Delete any full lines, which, start with a '#'
So, it only understands simple "full-line" comments.
How to write regex is beyond the scope of this QA.
When you pre-munge the text at let fixed =, use whatever regex or other technique you wish.
The JSON parsers are very finicky about what is at the start of a JSON block to parse - they DO NOT like characters other than "{" at the start (at least that's what I found with TouchJSON, and it sounds like your case with SBJson is similar).
So just take your string and eliminate any characters before the opening "{", then you can parse:
NSRange startJSONRange = [myJSONString rangeOfString:#"{"];
startJSONRange.length = myJSONString.length - startJSONRange.location;
NSString *correctJSONString = [myJSONString substringWithRange:startJSONRange];
// parse correctJSONString
That will work, but the REAL fix is to tell whoever is sending you JSON to cut out the nonsense and send real JSON.