Turning json variables into swift variables - json

My PHP server-side returns a JSON like this:
[{"scan_status":"ok","visitorData":[{"visitorCompany":"xyl","visitorStreet":"street","visitorBranche":"health","visitorEmail":"wesweatyoushop#gmail.com","lastmodified":"2014-12-15 14:18:55"}]}]
Now in Swift I would like to store this data, and for this I am trying to parse the data into Swift variables, however I got stuck.
do {
//check wat we get back
let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves )
let vData = jsonData[0]["visitorData"]
//let vCompany = vData["visitorCompany"]
print("Test vData: \(vData)")
}
This prints
Test vData: Optional(( { visitorStreet = street; visitorPhone = 01606478; visitorCompany = xyl; visitorBranche = Sports; visitorEmail = "health#gmail.com"; lastmodified = "2014-12-15 14:18:55"; } ))
but when I try to get visitorCompany with
let vCompany = vData["visitorCompany"]
I get a compile error:
Cannot subscript a value of type 'AnyObject?!' with an index of type 'String'
BTW, why do we see the equals sign in swift i.e. visitorStreet = street?

This is because the compiler doesn't know the type of your decoded objects.
Help the compiler using casting with if let:
do {
let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves )
if let vData = jsonData[0]["visitorData"] as? [[String:AnyObject]] {
if let vCompany = vData[0]["visitorCompany"] {
print(vCompany)
}
}
}

let vData = jsonData[0]["visitorData"] populates vData with a generic AnyObject?, because Swift can't know what kind of objects PHP returns in the JSON.
You need to do a an optional cast to another dictionary before you can use vData like you want: jsonData[0]["visitorData"] as? [String:AnyObject].
And because a conditional cast returns an optional, it's best you do an optional binding to unwrap that optional, resulting in a code similar to this:
if let vData = jsonData[0]["visitorData"] as? [String:AnyObject] {
//let vCompany = vData["visitorCompany"]
print("Test vData: \(vData)")
}
Or even better, as jsonData can not be an array, or it could be an empty array (the server malfunctions and sends an invalid json for example), you can go even further with the validation:
if let items = jsonData as? [[String:AnyObject]], vData = items.first?["visitorData"] {
//let vCompany = vData["visitorCompany"]
print("Test vData: \(vData)")
}
items = jsonData as? [[String:AnyObject]] fails if jsonData is not an array, while vData = items.first?["visitorData"] fails if items.first is nil (optional chaining here), or if items.first doesn't have a visitorData key.

Try with this:
let vData = jsonData[0]["visitorData"]![0] as! [String:AnyObject]

Related

Use Swift Decoder to pull attributes from JSON array

I have a JSON array created using this call:
guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [Any] else {
print("This is not JSON!!!")
return
}
I am trying to get elements from the JSON objects in the array to display them using the following code:
struct sWidget: Codable{
var createdBy: String
var createdDate: Date
var status: String
var widgetNumber: String
var updatedBy: String
var updatedDate: Date
}
do {
let decoder = JSONDecoder()
for (index, value) in json.enumerated() {
let currentWidget = try decoder.decode(sWidget.self, from: json[index] as! Data)
let currentNum = currentWidget.widgetNumber
//print(currentNum)
widgetNums.append(currentNum)
}
}
catch {
print("decoding error")
}
The code compiles but when I run it I get this error in the output:
Could not cast value of type '__NSDictionaryM' (0x1063c34f8) to
'NSData' (0x1063c1090). 2018-08-09 09:41:02.666713-0500
TruckMeterLogScanner[14259:1223764] Could not cast value of type
'__NSDictionaryM' (0x1063c34f8) to 'NSData' (0x1063c1090).
I am still investigating but any tips would be helpful.
Did you try that fetching objects like above mentioned? Because i see that you are using Codable. Fetching is very simple with that actually.
let yourObjectArray = JSONDecoder().decode([sWidget].self, data: json as! Data)
May be this line can be buggy but you can fetch them with one line.
Extending #Cemal BAYRI's answer:
JSONDecoder() throws, so make sure to either us try? or try (don't forget do-catch with try)
guard let data = content as? Data else {
return [sWidget]()
}
let jsonDecoder = JSONDecoder()
1. try?
let yourObjectArray = try? jsonDecoder.decode([sWidget].self, data: data)
2. try
do {
let yourObjectArray = try jsonDecoder.decode([sWidget].self, data: data)
} catch let error {
}
Note: You would need to take care of Data and Date formatting. Below is an example for Date:
jsonDecoder.dateDecodingStrategy = .iso8601
You can also check it out here

Parsing JSON with Swift (NSArray)

I'm trying to parse a JSON file in Xcode with Swift 3. My problem is getting the "values", 14.2. The error I get is "Type Any has no subscript member".
Here is my NSArray (Aka firstIndex):
{
level = 2;
levelType = hl;
name = t;
unit = Cel;
values = (
"14.2"
);
}
Here is my code:
if let parameters = test["parameters"] as? NSArray {
let firstIndex = parameters[0]
//print(firstIndex)
PROBLEM --> if let values = firstIndex["values"] as? Int {
}
}
The value for key values is also an array, represented by the parentheses.
The error occurred because NSArray does not provide type information.
In Swift use always native Array (here [[String:Any]]).
if let parameters = test["parameters"] as? [[String:Any]], let firstIndex = parameters.first {
if let values = firstIndex["values"] as? [String], let firstValue = values.first {
print(firstValue) // or Double(firstValue)!
}
}

Get JSON Element in Swift 3

Please excuse me if this is a simple question, but I am stuck. I have tried to read everything I can to work it out myself.
I am trying to extract a URL from JSON data, I get the JSON data fine and I can print it to the console, however I can't work out how to access the URL for the audio file.
This is the code I use to get the JSON:
let session = URLSession.shared
_ = session.dataTask(with: request, completionHandler: { data, response, error in
if let response = response,
let data = data,
let jsonData = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) {
if let dictionary = jsonData as? [String: Any] {
if let prounce = dictionary["pronunciations"] as? [String: Any]{
if let audioPath = prounce["audioFile"] as? String {
print(audioPath)
}
}
}
print(response)
print(jsonData)
} else {
print(error)
print(NSString.init(data: data!, encoding: String.Encoding.utf8.rawValue))
}
}).resume()
The output I get is:
metadata = {
provider = "Oxford University Press";
};
results = (
{
id = maladroit;
language = en;
lexicalEntries = (
{
entries = (
{
etymologies = (
"late 17th century: French"
);
grammaticalFeatures = (
{
text = Positive;
type = Degree;
}
);
senses = (
{
definitions = (
"inefficient or inept; clumsy:"
);
examples = (
{
text = "both men are unhappy about the maladroit way the matter has been handled";
}
);
id = "m_en_gb0494140.001";
}
);
}
);
language = en;
lexicalCategory = Adjective;
pronunciations = (
{
audioFile = "http://audio.oxforddictionaries.com/en/mp3/maladroit_gb_1.mp3";
dialects = (
"British English"
);
phoneticNotation = IPA;
phoneticSpelling = "\U02ccmal\U0259\U02c8dr\U0254\U026at";
}
);
text = maladroit;
}
);
type = headword;
word = maladroit;
}
);
}
I want to get the URL called audioFile in the pronunciations. Any help is much appreciated.
If my guess is right, your output shown above lacks opening brace { at the top of the output.
(I'm also assuming the output is taken from your print(jsonData).)
Your jsonData is a Dictionary containing two values:
A dictionary value for "metadata"
An array value for "results"
So, you cannot retrieve a value for "pronunciations" directly from jsonData (or dictionary).
You may need to:
Retrieve the value for "results" from jsonData, it's an Array
Choose one element from the "results", it's a Dictionary
Retrieve the value for "lexicalEntries" from the result, it's an Array
Choose one element from the "lexicalEntries", it's a Dictionary
Retrieve the value for "pronunciations" from the lexicalEntry, it's an Array
Choose one element from the "pronunciations", it's a Dictionary
Here, you can access the values in each pronunciation Dictionary. In code, you need to do something like this:
if
let dictionary = jsonData as? [String: Any],
let results = dictionary["results"] as? [[String: Any]],
//You need to choose one from "results"
!results.isEmpty, case let result = results[0],
let lexicalEntries = result["lexicalEntries"] as? [[String: Any]],
//You need to choose one from "lexicalEntries"
!lexicalEntries.isEmpty, case let lexicalEntry = lexicalEntries[0],
let pronunciations = lexicalEntry["pronunciations"] as? [[String: Any]],
//You need to choose one from "lexicalEntries"
!pronunciations.isEmpty, case let pronunciation = pronunciations[0]
{
//Here you can use `pronunciation` as a Dictionary containing "audioFile" and some others...
if let audioPath = pronunciation["audioFile"] as? String {
print(audioPath)
}
}
(You can use let result = results.first instead of !results.isEmpty, case let result = results[0], if you always use the first element for arrays. Other two lines starting from !...isEmpty, case let... as well.)
You need to dig into the target element from the outermost element step by step.

Swift :: Traverse a json string

Here is a string looks like a json.
let text2 = " [{ \"insertion_date\" :\""+"2015-07-31 11:21:04 +0000"+"\",\"mood\": \""+"Happy"+"\",\"temperature\": \""+"22"+"\"},{ \"insertion_date\" :\""+"2015-07-31 11:21:04 +0000"+"\",\"mood\": \""+"Sad"+"\",\"temperature\": \""+"22"+"\"}]"
I can access the whole string like this.
var data = text2.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
var localError: NSError?
var json3: AnyObject! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError)
println(json3)
But now I want to access individual element of this string, like - I want to access the second "mood" key, which has the value "Sad".
How can I access it?
You could use something like this:
var jsonArr: NSArray! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError) as! NSArray
for dict in jsonArr {
print(dict.objectForKey("mood"))
}
Which will print each of the moods.
Swift's native JSON parsing is a pain due to all the optional unwrapping:
if let days = json3 as? NSArray {
if let secondDay = days[1] as? NSDictionary {
let mood = secondDay["mood"] as! String
print(mood)
}
}
You can use other framework like SwiftyJson for an easier time.
You can also use Swift's native types instead of NSArray and NSDictionary, and cast the result of NSJSONSerialization as an array of dictionaries ([[String:AnyObject]]):
if let json3 = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: &localError) as? [[String:AnyObject]] {
for dict in json3 {
if let mood = dict["mood"] as? String {
println(mood)
}
}
}
Note also that you don't need to give specific options to NSJSONSerialization, you can pass nil in this case.
If you're absolutely certain that your values will always be of type String, you can also cast the result directly (and avoid later casts when accessing values):
if let json3 = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: &localError) as? [[String:String]] {
for dict in json3 {
if let mood = dict["mood"] {
println(mood)
}
}
}

Swift - Reading JSON File

I'm new to Swift - trying to read a JSON file from a URL. My attempt below.
The JSON looks valid - I tested it with JSONLint but it keeps crashing.
Thoughts?
func getRemoteJsonFile() -> NSDictionary {
//Create a new url
let remoteUrl:NSURL? = NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")
//check if its nil
if let actualRemoteUrl = remoteUrl {
//try to get the data
let filedata:NSData? = NSData(contentsOfURL: actualRemoteUrl)
//check if its nil
if let actualFileData = filedata {
//parse out the dictionaries
let jsonDict = NSJSONSerialization.JSONObjectWithData(actualFileData, options: NSJSONReadingOptions.AllowFragments, error: nil) as NSDictionary
return jsonDict
}
}
return NSDictionary()
}
This took me a second to figure out, so I don't blame you for missing it.
The JSON you linked to is minified, so it's difficult to see the structure. Let's take a look at (a fragment of) it after piping it through a prettifier:
[
{
"PlayerId":2501863,
"PlayerName":"Peyton Manning",
"PlayerTeam":"DEN",
"PlayerPosition":"QB",
"PlayerPassingYards":4727,
"PlayerPassingTDs":39,
"PlayerInterceptions":15,
"PlayerRushingYards":-24,
"PlayerRushingTDs":0,
"PlayerReceivingYards":0,
"PlayerReceivingTDs":0,
"PlayerReturnYards":0,
"PlayerReturnTDs":0,
"PlayerFumbleTDs":0,
"PlayerTwoPointConversions":2,
"PlayerFumblesLost":2,
"PlayerTeamLogo":"http://i.nflcdn.com/static/site/7.0/img/logos/teams-gloss-81x54/den.png"
}
]
Huh. It's encased in brackets, which means that it's an array.
It's an array, so you can't cast it as an NSDictionary. Instead, you could cast it as an NSArray, but why not use native Swift types?
Well, if you don't like types, you're about to find out, but I still think that this is a better way, because it forces you to think about the data you're parsing.
So we have the first part of our type definition for this function; it's an array ([]). What components is our array made up of? We could go with a simple NSDictionary, but we're doing full native types here, so let's use a native Swift dictionary.
To do that, we have to know the types of the dictionary (the syntax for a native dictionary type is [KeyType: ValueType]). Examining the JSON shows that all of the keys are Strings, but the values are of varying types, so we can use AnyObject.
That gives us a dictionary type of [String: AnyObject], and our entire JSON is an array of that, so the final type is [[String: AnyObject]] (wow).
Now that we have the proper type, we can modify the function you're using to parse the JSON a bit.
First of all, let's use our new type for the return and cast values. Then, let's make the return type optional in case something goes wrong and add an error variable to document that.
A cleaned up function would look something like this:
func getData() -> [[String: AnyObject]]? {
let data: NSData? = NSData(contentsOfURL: NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")!)
if let req: NSData = data {
var error: NSError?
if let JSON: [[String: AnyObject]] = NSJSONSerialization.JSONObjectWithData(req, options: NSJSONReadingOptions.AllowFragments, error: &error) as? [[String: AnyObject]] {
return JSON
}
}
return nil
}
That's it!
We can now call the function and extract values from our [[String: AnyObject]] (again, wow) like this:
if let data: [[String: AnyObject]] = getData() {
println(data[0]["PlayerName"]!) // Peyton Manning
}
Update your code with this:
func getRemoteJsonFile() -> [NSDictionary] {
// Create a new URL
let remoteUrl:NSURL? = NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")
let urlString:String = "\(remoteUrl)"
// Check if it's nil
if let actualRemoteUrl = remoteUrl {
// Try to get the data
let fileData:NSData? = NSData(contentsOfURL: actualRemoteUrl)
// Check if it's nil
if let actualFileData = fileData {
// Parse out the dictionaries
let arrayOfDictionaries:[NSDictionary]? = NSJSONSerialization.JSONObjectWithData(actualFileData, options: NSJSONReadingOptions.MutableContainers, error: nil) as [NSDictionary]?
if let actualArrayOfDictionaries = arrayOfDictionaries {
// Successfully parsed out array of dictionaries
return actualArrayOfDictionaries
}
}
}
return [NSDictionary]()
}
This is working fine for me.