Currently learning Swift and I'm new to parsing of json.
I'm trying to parse json using alamofire using swift 3. However Im not getting any response. How should I get the value of parameter1 or parameter 2 which were nested?
My json looks like this:
{ "data":{
"level1":{
"level2":{
"parameter1":"000000",
"parameter2":"00/00/00 00:00:00",
"parameter3":"00.0",
}
My swift code looks like this ,
func downloadDataDetails(completed: #escaping DownloadComplete) {
//Get data from URL
Alamofire.request("MY_URL").responseJSON { response in
let result = response.result
if let dict = result.value as? Dictionary<String , AnyObject> {
if let data = dict["data"] as? String {
if let level1 = dict["level1"] as? String {
if let level2 = dict["level2"] as? String? {
self._myValue = level2
}
}
}
}
completed()
}
I recommend you that use SwiftJson (https://cocoapods.org/pods/SwiftyJSON)
if you need to validate that something exist you can use .exist() (Return a boolean)
func test() {
let json: JSON = [ "data":[
"level1":[
"level2":[
"parameter1":"000000",
"parameter2":"00/00/00 00:00:00",
"parameter3":"00.0"
]
]
]
]
print(json) //Create a breakpoint here
}
If you have this JSON and you need to know if parameter1 exist:
(Put a break point in print(json))
(In the Console)
(lldb) po json["data"]["level1"]["level2"]["parameter1"].exists()
// response true
In the code would be:
if json["data"]["level1"]["level2"]["parameter1"].exists(){
}
If you need to get the value would be:
if json["data"]["level1"]["level2"]["parameter1"].exists(){
let parameter1 = json["data"]["level1"]["level2"]["parameter1"]
print(parameter1)
}
Complete example:
func test() {
let json: JSON = [ "data":[
"level1":[
"level2":[
"parameter1":"000000",
"parameter2":"00/00/00 00:00:00",
"parameter3":"00.0"
]
]
]
]
if json["data"]["level1"]["level2"]["parameter1"].exists(){
let parameter1 = json["data"]["level1"]["level2"]["parameter1"]
print(parameter1)
}
print(json["parameter1"])
}
The console log is:
000000
{
"data" : {
"level1" : {
"level2" : {
"parameter1" : "000000",
"parameter3" : "00.0",
"parameter2" : "00\/00\/00 00:00:00"
}
}
}
}
Related
While getting JSON data from my API, I can't get it to decode properly.
[
{
"success": "true",
"message": "testtt"
}
]
This is what my API output looks like.
As we can see, my PHP outputs the values as an top level array.
How can I read out this information in Swift 4?
let json = try JSONDecoder().decode([API].self, from: data)
returns:
success: "true", message: "testtt"
This is what the struct looks like:
struct API: Decodable{
let success: String
let message: String
init(jsont: [String: Any]){
success = jsont["success"] as? String ?? ""
message = jsont["message"] as? String ?? ""
}
}
But then I don't know how to read out this data further.
Any ideas?
There is no need to creat a custom initialiser. You just use the Array type [API].self when decoding your json:
struct API: Decodable{
let success: String
let message: String
}
let dataJSON = Data("""
[
{
"success": "true",
"message": "testtt"
}
]
""".utf8)
do {
if let result = try JSONDecoder().decode([API].self, from: dataJSON).first {
print(result.success)
print(result.message)
}
} catch {
print(error)
}
If you want to access make one more struct like
struct data: Decodable{
let API: [API]
}
Then in your program you must decode like below above
let json = try JSONDecoder().decode(data.self, from: data)
and access to them
data.API[i].success
data.API[i].message
My response from API is
items are.....****************** ("user_img", http://www.xxx/Content/Images/Products/NoImageAvailable.jpg)
items are.....****************** ("user_posts", 10)
items are.....****************** ("5", {
"post_id" : 135,
"post_img" : [
{
"guid" : "http:\/\/www.xxx\/wp- content\/uploads\/2016\/10\/IMG_1477393867.jpg"
}
]
})
items are.....****************** ("9", {
"post_id" : 143,
"post_img" : [
{
"guid" : "http:\/\/www.xxx\/wp-content\/uploads\/2016\/10\/IMG_1477453054.jpg"
}
]
})
items are.....****************** ("2", {
"post_id" : 129,
"post_img" : [
{
"guid" : "http:\/\/www.xxx\/wp- content\/uploads\/2016\/10\/IMG_1477280037.jpg"
}
]
})
items are.....****************** ("1", {
"post_id" : 112,
"post_img" : [
{
"guid" : "http:\/\/www.xxx\/wp-content\/uploads\/2016\/10\/IMG_1475494067.jpg"
}
]
})
items are.....****************** ("8", {
"post_id" : 141,
"post_img" : [
{
"guid" : "http:\/\/www.xxx\/wp- content\/uploads\/2016\/10\/IMG_1477452361.jpg"
}
]
})
function from where I called it
func getJSON(){
let getEndPoint: String = "http://xxx/api/get_user_profile_info/"
Alamofire.request(getEndPoint)
.responseJSON { response in
guard response.result.error == nil else {
// got an error in getting the data, need to handle it
print("error calling GET")
print(response.result.error!)
return
}
if let value = response.result.value {
let json = JSON(value)
// print(jsonM)
//print("message are***********************************************")
//print(json["message"].dictionary)
let message = json["message"].dictionary
for items in message! {
print("items are.....******************", items)
}
//DispatchQueue.main.async {
// self.afData = 1
// self.tableView.reloadData()
}
}
}
Model Class
class ProfileJSON {
var user_Image: URL?
var post_image:URL?
init(items: JSON) {
self.user_Image = items["user_img"].URL
let post_imgAA = items["post_img"].array
for itemsIMG in post_imgAA! {
self.post_image = itemsIMG["guid"].URL
}
}
}
I want to get the user_img to show as profile picture and the
post_img for showing picture in the CollectionViewCell . Finding it difficult to convert JSON Dictionary to Swift MutableObject . Any suggestion , any tutorial link would be great great help for me. I have just started to work with JSON from this month , finding it difficult.
In your ProfileJSON you need to create array of URL type for post_image because user_Image is once but post_image is coming multiple times and then you can get post_image from dictionary like this.
class ProfileJSON {
var user_Image: URL?
var post_image: [URL?] = [URL?]()
init(dic: [String: Any]) {
if let userUrl = dic["user_img"] as? String {
self.user_Image = URL(string: userUrl)
}
//For getting only number keys with ascending order
let keys = (Array(dic.keys) as [String]).filter { (Int($0) != nil) }.sorted {
(s1, s2) -> Bool in return s1.localizedStandardCompare(s2) == .orderedAscending
}
//Loop through the keys Array and append all your `post_image`.
for key in keys {
if let innerDic = dic[key] as? [String: Any],
let post_imgArray = innerDic["post_img"] as? [[String: Any]] {
for post in post_imgArray {
if let postUrl = post["guid"] as? String {
self.post_image.append(URL(string: postUrl))
}
}
}
}
}
}
Now create the object of ProfileJSON after initialization of message like this.
if let message = json["message"] as? [String: Any] {
let profileJSON = ProfileJSON(dic: message)
}
You can extract details from dictionary using DicitonaryObject.objectForKey("KEYNAME") as? Datatype .
Datatype would the of the value stored in that key.
Store it in a variable and use it wherever you want.
Does anyone know how to get the object at the index of an array, at the value for a specific key, and set it to a string?
I used to do this in Objective-C, but I can't quite figure out how to do it in Swift:
NSString *rT = [[self.rA objectAtIndex:0] valueForKey:#"Value"];
I've tried different things like this, but they don't work:
if let JSON = response.result.value {
print("JSON: \(JSON)")
var name: String? = self.rA[0].valueForKey("item_1") as? String
}
Endpoint:
[
{
"item_1": "Austin",
"item_2": "Texas"
}
]
Logging:
2016-04-01 13:35:42.787 A[66185:7391524] Response Array: (
{
"item_1" = Austin;
"item_2" = Texas;
}
)
I assume that you have something like array of dictionaries, So I made some test here it is sample code :
UPDATE
let jsonString = "[ { \"item_1\": \"Austin\", \"item_2\": \"Texas\" }, { \"item_3\": \"Austin3\", \"item_4\": \"Texas4\" } ]"
var array = []
do {
array = try NSJSONSerialization.JSONObjectWithData(jsonString.dataUsingEncoding(NSUTF8StringEncoding)!, options:.AllowFragments) as! NSArray
} catch {
print(error)
}
if let unwrappedValue = (array[0]["item_1"]){
print(unwrappedValue)
}
It will return an optional so it would be nice to use if let statement to unwrap value
For your usage this code should be like this :
if let JSON = response.result.value {
if let unwrappedValue = (JSON[0]["item_1"]){
print(unwrappedValue)
}
}
I am new to Swift but am trying to use a PHP based web service that returns a reply :-
{
responseData = {
1 = {
name = "3D SYSTEMS CORP";
result = success;
};
10 = {
name = "ABERCROMBIE & FITCH CO-CL A";
result = success;
};
};
}
I have assumed this is a valid JSON format but have not been able to work out how to load this data which is just a list of strings into an string array. I have trie code such as this to iterate one rte elements but with no success.
let url = urlComponents.URL
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {
(data, response, error) -> Void in
if let urlContnet = data
{
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContnet, options: NSJSONReadingOptions.MutableContainers)
if let nudges = jsonResult["responseData"] as? NSArray {
for nudge in nudges {
print(nudge)
}
}
}
catch
{
print("ERROR")
}
}
}
task.resume()
can anyone help
Thanks
Steve
There are two things here
Your JSON is malformed. You can check the validity of JSON at:
http://jsonlint.com
There are several issues in your JSON. The corrected JSON is here:
{
"responseData": {
"1": {
"name": "3D SYSTEMS CORP",
"result": "success"
},
"10": {
"name": "ABERCROMBIE & FITCH CO-CL A",
"result": "success"
}
}
}
responseData is a dictionary and not an array. Change your code to process this dictionary instead of array.
HTH
Using Alamofire and SwiftyJSON to retrieve some JSON is trivial:
Given JSON such as
{
"results": [
{
"id": "123",
"name": "Bob"
},
{
"id": "456",
"name": "Sally"
}
}
This function will work:
func loadSomeJSONData() {
Alamofire.request(.GET, "http://example.com/json/")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
if let firstName = json["results"][0]["name"].string {
println("first name: \(firstName)") // firstName will equal "Bob"
}
}
}
All well and good. My problem arises when I need to load JSON from a paged API, that is, when the data is collected from multiple calls to an API endpoint, where the JSON looks more like:
{
"currentPage": "1",
"totalPages": "6"
"results": [
{
"id": "123",
"name": "Bob"
},
{
"id": "456",
"name": "Sally"
}
]
}
and then the next block would look like:
{
"currentPage": "2",
"totalPages": "6"
"results": [
{
"id": "789",
"name": "Fred"
},
{
"id": "012",
"name": "Jane"
}
]
}
In this case, I can recursively call a function to gather all the "pages" but I'm not sure how to put all the JSON fragments together properly:
func loadSomeJSONDataFromPagedEndPoint(page : Int = 1) {
Alamofire.request(.GET, "http://example.com/json/" + page)
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
if let totalPages = json["totalPages"].description.toInt() {
if let currentPage = json["currentPage"].description.toInt() {
let pageOfJSON = json["results"]
// add pageOfJSON to allJSON somehow??
if currentPage < totalPages {
self.loadSomeJSONDataFromPagedEndPoint(page: currentPage+1)
} else {
// done loading all JSON pages
}
}
}
var allJSON
loadSomeJSONDataFromPagedEndPoint()
What I'd like to happen is to have the "results" portion of each JSON response eventually collected into a single array of objects (the { "id": "123", "name": "Bob"} objects)
Bonus question: I'm not sure why I need to do json["totalPages"].description.toInt() in order to get the value of totalPages, there must be a better way?
You have several questions in here, so let's take them one at a time.
I can't tell from your post if you get valid JSON back for each page call or whether you need to put them altogether to complete the JSON. So let's walk through both cases.
Option 1 - Valid JSON from each Page
You're already very close, you just need to tweak your JSON parsing a bit and store the results. Here's what this could look like.
class PagedDownloader {
var pagedResults = [AnyObject]()
func loadSomeJSONDataFromPagedEndPoint(page: Int) {
let request = Alamofire.request(.GET, "http://example.com/json/\(page)")
request.responseJSON { [weak self] _, _, jsonData, _ in
if let strongSelf = self {
let json = JSON(jsonData!)
let totalPages = json["totalPages"].stringValue.toInt()!
let currentPage = json["currentPage"].stringValue.toInt()!
let results = json["results"].arrayObject!
strongSelf.pagedResults += results
if currentPage < totalPages {
strongSelf.loadSomeJSONDataFromPagedEndPoint(currentPage + 1)
} else {
strongSelf.parsePagedResults()
}
}
}
}
func parsePagedResults() {
let json = JSON(pagedResults)
println(json)
}
}
You seem to know your way around SwiftyJSON so I'll let you handle the parsePagedResults implementation.
Option 2 - Pages must be assembled to create valid JSON
Paging JSON
First off, you can't parse partial JSON, it just won't work. The NSJSONSerialization will fail. This means that you can't use the responseJSON serializer with paged JSON because data will always be nil and error will always be the json serialization error. Long story short, you need cache all your data until it's valid JSON, then you can parse.
Storing Paged JSON
If you're going to store it, this is what it could look like as a simple example without Alamofire getting in the mix.
class Pager {
let page1 = "{\"currentPage\":\"1\",\"totalPages\":\"3\",\"results\":[{\"id\":\"123\",\"name\":\"Bob\"},"
let page2 = "{\"id\":\"456\",\"name\":\"Sally\"},{\"id\":\"234\",\"name\":\"Christian\"},"
let page3 = "{\"id\":\"567\",\"name\":\"Jerry\"},{\"id\":\"345\",\"name\":\"John\"}]}"
let pages: [String]
let jsonData: NSMutableData
init() {
self.pages = [page1, page2, page3]
self.jsonData = NSMutableData()
}
func downloadPages() {
for (index, page) in enumerate(pages) {
jsonData.appendData(page.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
}
let json = JSON(data: jsonData)
println(json)
if let totalPages = json["totalPages"].string?.toInt() {
println("Total Pages Value: \(totalPages)")
}
}
}
Your bonus question is answered at the end of that code chunk. You don't want to use description from SwiftyJSON, but instead the string optional cast and then optional chain into the toInt method.
Paging and Storing with Alamofire
Now that you have a simple example of how to write the JSON pages into data chunks, let's look at how that same approach could be used with the response serializer in Alamofire.
class Downloader {
var jsonData = NSMutableData()
var totalPagesDownloaded = 0
let totalPagesToDownload = 6
func loadSomeJSONDataFromPagedEndPoint() {
for page in 1...self.totalPagesToDownload {
let request = Alamofire.request(.GET, "http://example.com/json/\(page)")
request.response { [weak self] _, _, data, _ in
if let strongSelf = self {
strongSelf.jsonData.appendData(data as NSData)
++strongSelf.totalPagesDownloaded
if strongSelf.totalPagesDownloaded == strongSelf.totalPagesToDownload {
strongSelf.parseJSONData()
}
}
}
}
}
func parseJSONData() {
let json = JSON(data: jsonData)
println(json)
}
}
Parsing the Resulting JSON with SwiftyJSON
Inside the parseJSONData function, just use all the awesome features of SwiftyJSON to parse out the values you need.
I'm pretty sure that covers all your possible use cases and questions. Hope that helps!