Find values from JSON with unknown structure SWIFT - json

I read some questions about parsing JSON with unknown strucutre. But these questions are not in Swift and don't solve my issue.
I have a big JSON file that I obtain with a Alamofire HTTP request. Here is it: https://pastebin.com/Y7cWSWAt
In fact, the JSON could change with requests I do. It will not always be the same structure depending of the user (for example). However, there will be the same keys and values.
So I know the keys in the JSON to find the values, their will be always the same. But I don't know how to access them. Moreover, my JSON has an Any type (I can't give him a specific type because I don't know it).
My question: Is there a solution to find values with this? Can I loop throw all the JSON values to find specific keys and values? Is it better to work with JSON file or Dictionaries? I have no code yet because I have no idea. Thanks mates!

if you want to use just NSDictionary this not problem but when return array json your app will be crash because variable type not be equals.
let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments)
if json != nil {
let obj = json as? NSDictionary
}
//this part write all key and value
for (key, value) in obj {
print("Property: \"\(key as String)\", Value: \"\(value as String)\"")
}
input: {"test1":"value1","test2":"value2"}
output:
Property: "test1", Value: "value1"
Property: "test2", Value: "value2"

Related

Swift 3 parsing values from a json file

Swift 3 , Xcode8.2.1,
I'm trying to extract specific values from a json file in the project. The name of the file is city.list.json, and the syntax of the json file is as follows:
{"_id":707860,"name":"Hurzuf","country":"UA","coord":{"lon":34.283333,"lat":44.549999}}
{"_id":519188,"name":"Novinki","country":"RU","coord":{"lon":37.666668,"lat":55.683334}}
The input I have is the country name and i need the id value or the country code relevant returned as a string.
I get an error:
"Type 'Any?' has no subscript members",
The method I wrote:
private func findCountryCodeBy(location: String)->String{
var result:String="";
let bundle = Bundle(for: type(of: self));
if let theURL = bundle.url(forResource: "city.list", withExtension: "json") {
do {
let data = try Data(contentsOf: theURL);
if let parsedData = try? JSONSerialization.jsonObject(with: data, options:[]) as! [String:Any] {
result = parsedData["_id"][location][0] as! String;
}
} catch {
print(error);
result = "error";
}
}
return result;
}
That is not valid JSON. I think the nearest valid JSON equivalent would be EITHER a JSON list like:
[
{"_id":707860,"name":"Hurzuf","country":"UA","coord":{"lon":34.283333,"lat":44.549999}},
{"_id":519188,"name":"Novinki","country":"RU","coord":{"lon":37.666668,"lat":55.683334}}
]
Ie, a list enclosed within square brackets with each item separated by a comma.
OR a JSON dictionary:
{
"707860": {"name":"Hurzuf","country":"UA","coord":{"lon":34.283333,"lat":44.549999}},
"519188": {"name":"Novinki","country":"RU","coord":{"lon":37.666668,"lat":55.683334}}
}
Ie, a dictionary enclosed within curly brackets with the key (in this case I've used your _id as the key) before the : and the value (a dictionary of all the other items" after the :.
(Newlines, tabs, whitespace are ignored, I've just included them to make it obvious what I've done).
I think that the dictionary version may suit your code better, but it depends on what else you want to do with the data. A list may suit some situations better.
I wrote a quick Python script to simply read JSON from a file (and not do anything else with it), and it produced a parsing error for the not-quite-JSON that you had, but it worked fine on both of my JSON examples, above.
NB: If you do NOT have control over the format of the file you are reading (ie, if you are receiving it from some other source which cannot produce it in any other format) then you will have to either modify the format of the file after you receiv it to make it valid JSON, OR you will have to use something other than JSONSerialization to read it. You could modify it by replacing all occurrences of }{ or }\n{ with },{ and then put [ at the beginning and ] at the end. That should do the job for converting this particular file to valid JSON for a list. Converting to a dictionary would be a little more involved.
Ideally though, you may have control over the file format yourself, in which case, just change whatever generates the file to produce correct JSON in the first place.
Once you have your valid JSON and parsed it into your parsedData variable, you'll then need to fix this line:
result = parsedData["_id"][location][0] as! String;
Assuming that location is the the equivalent of the _id string in the JSON, then you may be able to use the dictionary version of the JSON above and replace that line with something like:
result = parsedData[location]["country"];
However, if location is not the _id string in the JSON, then you'd be better off using the list version of the JSON above, and use a for loop to compare the values of each list item (or use a dictionary version of the JSON keyed on whatever location actually relates to in the JSON).

How do you create a SwiftyJSON dictionary out of optional AnyObjects?

I recently ran into a very time-consuming issue for a simple task of creating a JSON dictionary with optional objects. I'm using SwiftyJSON.
If I create the following dictionary, I get thrown errors
JSON type doesn't have an initializer that supports Dictionary<String,AnyObject?>
But if I simply change AnyObject to an non-optional type, it works.
var dict: Dictionary <String, AnyObject?> = [
"title" : title as? AnyObject,
"date" : date as? AnyObject,
]
var jsonData: JSON = JSON(dict) // this is where I get the error
I need to actually a JSON data set that potentially has nil values, but it seems like SwiftyJSON doesn't allow it.
Is there any way I can create a JSON dictionary with optional objects using SwiftyJSON?
Neither any key nor any value of a Swift Dictionary can be nil.
You could declare the variable as optional
var dict: Dictionary <String, AnyObject>?

Swift: If I copy AnyObject that is JSON data, will the data be copied or passed by reference?

I get JSON data as a result of NSURLSession.sharedSession().dataTaskWithRequest and deserialize it to AnyObject:
var error: NSError?
let jsonObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: &error)
I want to pass it to a completion handler for parsing jsonObject into structured data.
Question: will jsonObject be passed by reference or deep-copied? The question arises since Array and Dictionary that JSON consists of are value-type in Swift.
I found this answer to related question that says that objects inside Foundation are indeed NSArray and NSDictionary, i.e. reference types. Is it the same with JSON data?
Class objects (AnyObject) are always passed by reference.
JSON result data consists of NSArray and NSDictionary which are
reference-type. If you get result data into jsonObject which is
of type AnyObject? and you passed it in completion handler then
it will depends upon type of variable inside completion handler
that whether it will passed by refrence or deep values will copied.
If inside completion handler variable for catching jsonObject is
of type AnyObject then reference will passed and if completion
handler variable is of particular Array or Dictionary type
then it will deeply copied jsonObject's value.
Also one more thing is that if your completion handler variable is of particular type then you should know the JSON result format already otherwise it will not parsed properly.

Swift json parsing error: Could not cast value of type NSCFConstantString to NSArray

I have some problems with parsing json using swift code.
json example
{"responce": "ok","orders": [{"id":"1"), {"id":"2"}, {"id":"3"} ]}
and this code working fine
let dataArray: NSArray = jsonResult["orders"] as! NSArray
but if I get {"responce": "ok","orders": ""} I got the error: Could not cast value of type __NSCFConstantString (0x10c7bfc78) to NSArray (0x10c7c0470).
Can I somehow check if value is array or not to do not crashed?
Yes you can check if the value is a NSArray by doing this:
if let dataArray = jsonResult["orders"] as? NSArray {
}
If the result of jsonResult["orders"] is a NSArray then dataArray will be set and you will go into the if statement.
This error is most likely caused by the response you are getting back from what I assume is a server not being JSON, but being something like an HTML/XML response saying that the server either could not be reached, or that your query/post request was invalid (hence the fact that the value was an "NSCFConstantString").
Using James' answer is a perfectly good way to check that the value is an Array, but you might want to test your requests using a program like Postman to see what he response is, and then hard code a way to handle that error on the user side.

JSON Deserialization in Swift

After reading up a bit on collections, I began to wonder if json deserialization was going to be an issue given that collections need to specify a type for the values they contain. And in the case of dictionaries, one would need to specify the type for both the key and the value.
After a bit of experimentation, I found that the following works:
let jsonString = "{\"bool\": true, \"num\": 1,\"string\": \"a string\"}"
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let json : AnyObject! = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil)
let valid = NSJSONSerialization.isValidJSONObject(json)
And when I use a playground (or am in the REPL), I get the following when printing out the contents of the json object:
["num": 1, "string": "a string", "bool": 1]
My question: is there may be a better way to handle this?
I'd suggest typing your json object a little more:
let json = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil) as? Dictionary<String, AnyObject?>
This will help you access elements by their key.