Create a JSON document from NSObject with SwiftyJSON - json

I'm trying to create a JSON array of objects, to manipulate and save in UserPreferences after, like this:
[
{
"id" : "01",
"title" : "Title"
},
{
"id" : "02",
"title": "Title 02"
}
]
this is my NSObject class:
class Item: NSObject {
var _id: String = ""
var _title: String = ""
var id: String {
get {
return _id
}
set {
_id = newValue
}
}
var title: String {
get {
return _title
}
set {
_title = newValue
}
}
}
And I have this code to convert to JSON using SwiftyJson, but I cant make this like a array
var item: [Item] = ["array of itens already setted"]
var json: JSON = JSON([:])
for item in list {
json["id"].string = item.id
json["title"].string = item.title
}
This code return just the last item of array:
{
"id" : "01",
"title" : "Title"
}

Problem lies here , as loop iterates you are setting values in same object.
var item: [Item] = ["array of itens already setted"]
var json: [JSON] = [JSON([:])]. -----> this should be array not just object
for item in list {
json["id"].string = item.id
json["title"].string = item.title
}
instead use this :
var item: [Item] = ["array of itens already setted"]
var json: [JSON] = [JSON([:])]. -----> json array
for item in list {
let jsonTemp: JSON = JSON([:])
jsonTemp["id"].string = item.id
jsonTemp["title"].string = item.title
json.append(jsonTemp)
}
print("[JSON OBJECT Count :: \(json.count), Informations are : \(json)]")

I'd suggest that class Item adopts Codable protocol.
Then have JSONEncoder do the job. That way, you can even nest the resulting JSON inside more complex type.
Also check this out for how to customize key names.
let items = [Item(), Item()]
items[0].id = "01"
items[0].title = "Title"
items[1].id = "02"
items[1].title = "Title 02"
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
print(String(data: try encoder.encode(items), encoding: .ascii)!)
/* result
[
{
"_id" : "01",
"_title" : "Title"
},
{
"_id" : "02",
"_title" : "Title 02"
}
]
*/

Related

How to initialize array inside json in a NSObject class in Swift 4?

I have a API response in JSON format like this :
{
"item": [
{
"item_id": 484,
"user_id": 123,
"lucky_number": -1,
"liked_user": [
{
"user_id": 2
},
{
"user_id": 1
}
],
"modified_time": "0000-09-19 10:24:02"
}
]
}
I know how to parse the result for item_id,user_id and others in the result above.But how to get the result for liked_user in the response above,which is an array?
Now my NSObject class look like this:
class Item : NSObject {
var itemtId :Int
var userId : Int
var luckyNumber : Int
var itemCreatedAt : String
init?(dict: [String :JSON]) {
self.itemId = dict["item_id"]?.int ?? 0
self.userId = dict["user_id"]?.int ?? 0
self.luckyNumber = dict["lucky_number"]?.int ?? 0
self.itemCreatedAt = dict["modified_time"]?.string ?? ""
}
}
And then after I make a API call,I parse the response to this class like this :
var items = [Item]()
My ApiCall success .... {
let json = JSON(result) // this is SwiftyJson
guard let itemArr = json["item"].array else{
return
}
for item in itemArr {
if let item = item.dictionary,let feed = Item.init(dict: item){
self.items.append(feed)
}
}
So my question is,in this scenario,how can I parse the data in liked_user and store it in my Item class?
With your given code a solution is to create an array likedUserIDs in the class
class Item : NSObject {
var itemtId :Int
var userId : Int
var luckyNumber : Int
var itemCreatedAt : String
var likedUserIDs = [Int]()
init?(dict: [String :JSON]) {
self.itemId = dict["item_id"]?.int ?? 0
self.userId = dict["user_id"]?.int ?? 0
self.luckyNumber = dict["lucky_number"]?.int ?? 0
self.itemCreatedAt = dict["modified_time"]?.string ?? ""
if let likedUsers = dict["liked_user"]?.array {
for likedUser in likedUsers {
likedUserIDs.append(likedUser["user_id"].intValue)
}
}
}
}
However in Swift 4 I recommend the (De)Codable protocol und use structs to parse the JSON.
SwiftyJSON is not needed (anymore).
struct Root : Decodable {
let item : [Item]
}
struct Item : Decodable {
let itemId :Int
let userId : Int
let luckyNumber : Int
let itemCreatedAt : String
let likedUsers : [LikedUser]
private enum CodingKeys : String, CodingKey {
case itemId = "item_id"
case userId = "user_id"
case luckyNumber = "lucky_number"
case itemCreatedAt = "modified_time"
case likedUsers = "liked_user"
}
struct LikedUser : Decodable {
let userID : Int
private enum CodingKeys : String, CodingKey { case userID = "user_id" }
}
}
And decode it passing the raw JSON as Data object
do {
let result = try decoder.decode(Root.self, from: data)
print(result)
} catch {
print("error: ", error)
}
I think the result for liked_user is a array that contains two Json object.
Below code block will be helpful to you.
let liked_users = json["liked_user"].array
for liked_user in liked_users {
let user_id = liked_user["user_id"].int
}
You can save the user_id to a array in your Item class.
class Item : NSObject {
var itemId :Int
var userId : Int
var luckyNumber : Int
var itemCreatedAt : String
var useridArray: [Int] = []
init?(dict: [String :JSON]) {
self.itemId = dict["item_id"]?.int ?? 0
self.userId = dict["user_id"]?.int ?? 0
self.luckyNumber = dict["lucky_number"]?.int ?? 0
self.itemCreatedAt = dict["modified_time"]?.string ?? ""
if let liked_users = dict["liked_user"]?.array {
for liked_user in liked_users {
if let userid = liked_user["user_id"]?.int {
useridArray.append(userid)
}
}
}
}
}
Create a likedUsersArray var in your Item object and then parse the array of dictionaries in this way:
class Item : NSObject {
var itemtId :Int
var userId : Int
var luckyNumber : Int
var itemCreatedAt : String
var likedUsersArray: [Int] = []
init?(dict: [String :JSON]) {
self.itemId = dict["item_id"]?.int ?? 0
self.userId = dict["user_id"]?.int ?? 0
self.luckyNumber = dict["lucky_number"]?.int ?? 0
self.itemCreatedAt = dict["modified_time"]?.string ?? ""
for likedUserDict in dict["liked_user"]?.array {
if let dict = likedUserDict?.dictionary, let likedUserId = dict["user_id"].int {
self.likedUsersArray.append(likedUserId)
}
}
}
}

Unable to parse json using alamofire using closures

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"
}
}
}
}

having difficulty fetching JSON Dictionary

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.

Realm + Swift, nested JSON

I have a problem since last 2 days. I can't get my JSON transformed to a Realm Object.
I have a json like below:
{
"gender" : "male",
"id" : "123456789",
"age_range" : {
"min" : 21
},
"last_name" : "LastName"
}
I have this Realm Models:
class UserObject: Object {
dynamic var userId: String = ""
dynamic var lastName: String?
dynamic var gender: String?
var ageRange = List<AgeRangeObject>()
required convenience init?(_ map: Map) {
self.init()
}
}
class AgeRangeObject: Object {
dynamic var min: Int = 0
}
And the way I am trying to create an instance of this model with ObjectMapper to parse json to dictionary and then create the model instance:
let userJSONModel = Mapper<User>().map(jsonString)
let realm = try! Realm()
do {
try realm.write {
let dict: [String : AnyObject] = [
"userId" : (userJSONModel?.userId)!,
"ageRange" : (userJSONModel?.ageRange)!,
"lastName" : (userJSONModel?.lastName)!,
"gender" : (userJSONModel?.gender)!
]
let userModel = UserObject(value: dict)
realm.add(userModel)
}
} catch {
print("Exception")
}
The problem occurs on this line: let userModel = UserObject(value: dict)
I get the folowing error:
*** Terminating app due to uncaught exception 'RLMException', reason: 'Invalid value 'min' to initialize object of type 'AgeRangeObject': missing key 'min''
I was looking on the stackoverflow:
Nested Arrays throwing error in realm.create(value: JSON) for Swift
How to convert Realm object to JSON with nested NSDate properties?
but my case is different.
Do you know what's the problem with that age range dictionary? Why it can't parse it well?
Thank you.
In your JSON, ageRange is a dictionary, whereas the UserObject.ageRange property is a List<AgeRangeObject>. You have mismatched models.
You either need to update your models to reflect the structure of your JSON:
var ageRange = List<AgeRangeObject>()
becomes
dynamic var ageRange: AgeRangeObject? = nil
or vice versa, update your JSON to reflect the structure of your models:
{
"gender" : "male",
"id" : "123456789",
"age_range" : [{
"min" : 21
}],
"last_name" : "LastName"
}
{
"key1" : "value1",
"key2" : "value2",
"array1" : [{
"key" : value
}],
"key3" : "value3"
}
For this you could use ObjectMapper's TransformType.
Reference: https://github.com/APUtils/ObjectMapperAdditions
My Code:
#objcMembers class RealmObject: Object, Mappable {
dynamic var listValues = List<MyRealmObject>()
required convenience init?(map: Map) {
self.init()
}
// Mappable
func mapping(map: Map) {
listValues <- (map["listValues"], RealmlistObjectTransform())
}
}
#objcMembers class MyRealmObject: Object, Mappable {
required convenience init?(map: Map) {
self.init()
}
// Mappable
func mapping(map: Map) {
}
}
class RealmlistObjectTransform: TransformType {
typealias Object = List<MyRealmObject> // My Realm Object here
typealias JSON = [[String: Any]] // Dictionary here
func transformFromJSON(_ value: Any?) -> List<MyRealmObject>? {
let list = List<MyRealmObject>()
if let actors = value as? [[String: Any]] {
let objects = Array<MyRealmObject>(JSONArray: actors)
list.append(objectsIn: objects)
}
return list
}
func transformToJSON(_ value: List<MyRealmObject>?) -> [[String: Any]]? {
if let actors = value?.sorted(byKeyPath: "").toArray(ofType: MyRealmObject.self).toJSON() {
return actors
}
return nil
}
}

simple way to map a json collection response into swift object class

Ive tried a lot of libraries, Alamofire, JsonHelper, ObjectMapper etc..., but unfortunately, Ive coundn't map a json collection response into an object class.
Im developing an IOS 8 App with swift 1.2 and xcode 6.3, and two classes of my model are:
Club.swift
class Club {
var id: String = ""
var name: String = ""
var imageUrl: String = ""
var hasVip: Bool = false
var desc: String = ""
var location: [Location] = []
}
Location.swift
class Location {
var country: String = ""
var city: String = ""
var address: String = ""
var zip: String = ""
var underground: [String] = []
}
I have another class to request to my API:
apliClient.swift
class ApiClient {
var clubs = [Club]?()
func getList(completionHandler: ([JSON]) -> ()) {
let URL = NSURL(string: "https://api.com/v1/clubs")
let mutableURLRequest = NSMutableURLRequest(URL: URL!)
mutableURLRequest.setValue("Content-Type", forHTTPHeaderField: "application/json")
mutableURLRequest.HTTPMethod = "GET"
mutableURLRequest.setValue("Bearer R01.iNsG3xjv/r1LDkhkGOANPv53xqUFDkPM0en5LIDxx875fBjdUZLn1jtUlKVJqVjsNwDe1Oqu2WuzjpaYbiWWhw==", forHTTPHeaderField: "Authorization")
let manager = Alamofire.Manager.sharedInstance
let request = manager.request(mutableURLRequest)
request.responseJSON { (request, response, json , error) in
if (json != nil){
var jsonObj = JSON(json!)
if let data = jsonObj["hits"].arrayValue as [JSON]?{
completionHandler(data)
}
}
}
}
}
and I think, there is a simple way to mapping objects in swift. I would like to know, how I can return the completionHandler(data) converted into a [Club] object?
let data = jsonObj["hits"].arrayValue as [JSON]? is
[{
"_id" : "5470def9e0c0be27780121d7",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/5470def9e0c0be27780121d7_180.png",
"name" : "Mondo",
"hasVip" : false,
"location" : {
"city" : "Madrid"
}
}, {
"_id" : "540b2ff281b30f3504a1c72f",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540b2ff281b30f3504a1c72f_180.png",
"name" : "Teatro Kapital",
"hasVippler" : false,
"location" : {
"address" : "Atocha, 125",
"city" : "Madrid"
}
}, {
"_id" : "540cd44581b30f3504a1c73b",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540cd44581b30f3504a1c73b_180.png",
"name" : "Charada",
"hasVippler" : false,
"location" : {
"address" : "La Bola, 13",
"city" : "Madrid"
}
}]
You can do this with the ObjectMapper library that you mentioned above. Simply create the Club class and make sure it implements the Mappable protocol. Then you can use ObjectMapper as follows to map the data:
let clubs = Mapper<Club>().mapArray(JSONString)
Full disclosure: I am the author of ObjectMapper.
With Swift 2.0, it is now possible, simple copy your json to http://www.json4swift.com and the swift models with entire key-value mapping will be auto generated, all you need to do is instantiate the models by passing either array or dictionary out of your Json.
For objective-c try JSONModel it can be what you are looking for...
and here you can find some more example of using it
note this about swift (from JSONModel's GitHub page):
Swift works in a different way under the hood than Objective-C. Therefore I can't find a way to re-create JSONModel in Swift. JSONModel in Objective-C works in Swift apps through CocoaPods or as an imported Objective-C library.
Update:
Check ups this blog post from apple about Working with JSON in Swift
https://developer.apple.com/swift/blog/?id=37