Convert json response to Realm object - json

hi I am trying to convert json response from a web service to realm object and insert it into realm database using object mapper but returns objects with null values
Alamofire.request(url).responseJSON { response in
let products = Mapper<ProRealm>().map(JSONObject:response.result.value)
print("products",products) // object with null values
}
My ProRealm class is
class ProRealm: Object, Mappable {
dynamic var additives_count: String?
var rating: String?
var updated: Bool = false;
var name: String?
var barcode: String?
var product_key: String?
var hazard_count: String?
var state: String?
var no_of_users_rated: String?
var thumbnail: String?
var overall_rating : String?
var is_food_or_beverage : Bool = false
//Impl. of Mappable protocol
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
additives_count <- map["additives_count"]
rating <- map["rating"]
updated <- map["updated"]
name <- map["name"]
barcode <- map["barcode"]
product_key <- map["product_key"]
hazard_count <- map["hazard_count"]
state <- map["state"]
no_of_users_rated <- map["no_of_users_rated"]
thumbnail <- map["thumbnail"]
overall_rating <- map["overall_rating"]
is_food_or_beverage <- map["is_food_or_beverage"]
}
}
In Json response, the products are inside the products key. please advice how to convert it into realm object
["kind": aWareInternalAPI#productsItem, "products":(
{
"additives_count" = 3;
barcode = 12345;
"hazard_count" = 0;
"is_food_or_beverage" = 1;
name = "Water Bottle";
"no_of_users_rated" = 1;
"overall_rating" = "0.0";
"product_key" = "ahdzfmF3YXJlLWJhY2tlbmQtc3RhZ2luZ3IlCxIEVXNlchiAgICgvoOTCQwLEgdQcm9kdWN0GICAgIDE_JAKDA";
rating = 0;
state = "OCR_PROCESSING";
thumbnail = "http://lh3.googleusercontent.com/dMxwgSQB02osZJJex4S57iupaMT9tjDYZaD7mweJUjYmI1KNEcZZe1syBwrRs1GbYdZNrRUtQwRYUwXiAEscGNYH-J9f3gJOXYO1rQ=s150";
updated = 1;
},
{
"additives_count" = 0;
barcode = 53647825898248485;
"hazard_count" = 0;
"is_food_or_beverage" = 1;
"no_of_users_rated" = 0;
"overall_rating" = "0.0";
"product_key" = ahdzfmF3YXJlLWJhY2tlbmQtc3RhZ2luZ3IlCxIEVXNlchiAgICgvoOTCQwLEgdQcm9kdWN0GICAgICumYAJDA;
rating = "";
state = "OCR_PROCESSING";
thumbnail = "http://lh3.googleusercontent.com/0D55ZXkG8Ua5ULDK69Po-IHeDPIfXZHOi7LlLURoc1qZzmNst57xUMQSPzWTW5miSDglc5wKDA4QlvLvnD6aMOqIHcwlj_HY-Hs=s150";
updated = 1;
})]
Please advice

All your properties except updated and is_food_or_beverage are nilable, so there is a possibility that they might be nil. If you know that they should have a value and you´re still getting nil make sure to check the keys in your mapping function.
Update:
I´m not 100% sure of how this works with the syntax with Mappable, but you need to do it something like this:
class Products: Object, Mappable {
let products: [Product]?
//Impl. of Mappable protocol
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
products <- map["products"]
}
}
class Product: Object, Mappable {
dynamic var additives_count: String?
var rating: String?
var updated: Bool = false;
var name: String?
var barcode: String?
var product_key: String?
var hazard_count: String?
var state: String?
var no_of_users_rated: String?
var thumbnail: String?
var overall_rating : String?
var is_food_or_beverage : Bool = false
//Impl. of Mappable protocol
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
additives_count <- map["additives_count"]
rating <- map["rating"]
updated <- map["updated"]
name <- map["name"]
barcode <- map["barcode"]
product_key <- map["product_key"]
hazard_count <- map["hazard_count"]
state <- map["state"]
no_of_users_rated <- map["no_of_users_rated"]
thumbnail <- map["thumbnail"]
overall_rating <- map["overall_rating"]
is_food_or_beverage <- map["is_food_or_beverage"]
}
}
So create two different classes one that holds an array of products and the other is a Product.

Related

Mapping local JSON file to Realm object using Object Mapper

I have local JSON file and i am trying to map that json to realm object using ObjectMapper_Realm library.
var totalLessonArray = [Lesson]()
if let path = Bundle.main.path(forResource: "data", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: [])
totalLessonArray = Mapper<Lesson>().mapArray(JSONArray: jsonResult as! [[String : Any]])
} catch {
// handle error
}
}
When i print totalLessonArray i get following error in my 4 level deep list
//...
quizScore = 0;
quizQuestions = List<Question> <0x6000022e4d80> (
[0] Question {
question = What are the synonym of sympathy? Drag and drop in the box below.;
answer = <Maximum depth exceeded>;
}
);
//...
This is the model file
class Lesson : Object, Mappable{
#objc dynamic var lessonNumber : Int = 0
#objc dynamic var totalChapter : Int = 0
#objc dynamic var completedChapter : Int = 0
#objc dynamic var quizTaken : Bool = false
#objc dynamic var isFinished : Bool = false
#objc dynamic var isCurrentlyWatchingLesson : Bool = false
#objc dynamic var currentlyWatchingChapter : Int = 0
#objc dynamic var lessonTitle : String = ""
#objc dynamic var quizScore : Int = 0
var chapter = List<Chapter>()
var quizQuestions = List<Question>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
lessonNumber <- map["lessonNumber"]
totalChapter <- map["totalChapter"]
completedChapter <- map["completedChapter"]
quizTaken <- map["quizTaken"]
isFinished <- map["isFinished"]
isCurrentlyWatchingLesson <- map["isCurrentlyWatchingLesson"]
currentlyWatchingChapter <- map["currentlyWatchingChapter"]
lessonTitle <- map["lessonTitle"]
quizScore <- map["quizScore"]
chapter <- (map["chapter"], ListTransform<Chapter>())
quizQuestions <- (map["quizQuestions"], ListTransform<Question>())
}
}
I have json which is 4-5 level down deep. Am i doing something wrong?

Parsing JSON & work with ObjectMapper,SwiftRealm,ObjectMapperAdditions,ObjectMapperAdditionsRealm & Alamofire

regards, I am trying to serialize a json from the placeHolder page, the users one (https://jsonplaceholder.typicode.com/ users), I am trying to use the following libraries together:
-> Alamofire
-> RealmSwift
-> ObjectMapper
-> ObjectMapperAdditions
-> ObjectMapper_Realm
Here is the model that I am defining, but the problem is that SwiftRealm does not support the ArrayDictionary data type, as you try changing the type in the COMPANY & ADDRESS attributes, which are of the array type, try to put it as List , [Address1]?, Address1, List , etc. but it does not work, it always marks the same error "The operation could not be completed, ObjectMapper could not serialize the response.", so it can be received as an object but it can not be saved in RealmSwift or failing that What to do, but you will also receive as JSON or ARRAY and it is also not possible to serialize it, have you implemented something like that?
import Foundation
import RealmSwift
import ObjectMapper
import ObjectMapperAdditions
import ObjectMapper_Realm
class PlaceHolderClass: Object, Mappable {
#objc dynamic var id = 0
#objc dynamic var name = ""
#objc dynamic var username = ""
#objc dynamic var email = ""
var address: Address1?
#objc dynamic var phone = ""
#objc dynamic var website = ""
var company: Company?
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
username <- map["username"]
email <- map["email"]
address <- map["address"]
phone <- map["phone"]
website <- map["website"]
company <- map["company"]
}
}
class Address1: Object, Mappable {
var street = List<String>()
var suite = List<String>()
var city = List<String>()
var zipcode = List<String>()
var geo: Geo?
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
street <- map["street"]
suite <- map["suite"]
city <- map["city"]
zipcode <- map["zipcode"]
geo <- map["geo"]
}
}
class Geo: Object, Mappable {
var lat = List<String>()
var lng = List<String>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
lat <- map["lat"]
lng <- map["lng"]
}
}
class Company: Object, Mappable {
var name = List<String>()
var catchPhrase = List<String>()
var bs = List<String>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
name <- map["name"]
catchPhrase <- map["catchPhrase"]
bs <- map["bs"]
}
}
My request Code:
func requestPlaceHolder(){
let URL = "https://jsonplaceholder.typicode.com/users"
Alamofire.request(URL).responseObject{ (response: DataResponse<PlaceHolderClass>) in
switch response.result {
case .success(let objects):
let realm = try! Realm()
let myPlaceHolderResponse = PlaceHolderClass(value: response.result.value)
try! realm.write {
realm.add(myPlaceHolderResponse, update: true)
print("se agrego correctamente")
}
print(Realm.Configuration.defaultConfiguration.fileURL)
case .failure(let error):
print("Ocurrio el siguiente error \(error.localizedDescription)")
}
print(Realm.Configuration.defaultConfiguration.fileURL)
}
}

how I can call variable in mappable class from main view

I have three class one is the main view and here is the code of it
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
getJSONS().getLowerJSON()
setupUI()
}
func setupUI(){
print(lowerData.companyName)
}
}
getsJSONS call an function that call almofire to handle json as in this code
class getJSONS {
func getLowerJSON(){
let URL = "http://tickerchart.com/interview/company-details.json"
Alamofire.request(URL).responseObject { (response: DataResponse<lowereViewResponse>) in
let lowereViewResponse = response.result.value
}
}
}
lowereViewResponse call the mappable class as in this code
class lowereViewResponse: NSObject, Mappable {
var companyName : String?
var symbol : String?
var tradesCount : Int?
var high : Int?
var low : Int?
var volume : Int?
var amount : Int?
override init() {
super.init()
}
convenience required init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
companyName <- map["company-name"]
symbol <- map["symbol"]
tradesCount <- map["trades-count"]
high <- map["high"]
low <- map["low"]
volume <- map["volume"]
}
required init?(map: Map){
companyName = ""
symbol = ""
tradesCount = 0
high = 0
low = 0
volume = 0
amount = 0
price = 0
}
}
all imports are done
I am trying to call class lowereViewResponse to access the variable but I get null when this is exited "print(lowerData.companyName)"
You can use like following:
let lowereViewObject: lowereViewResponse = Mapper< lowereViewResponse >().map(JSON: result)

Handling 2 keys for same value in single class

I have a use case in which I get score form JSON using
let score = json["score_test"].arrayValue.map {Score.decode(json: $0)}
I have to reuse this class for a response in which only key for the value changes i.e
let score = json["score"].arrayValue.map {Score.decode(json: $0)}
Is there a way to achieve this so that I get the data of Score object whether the key is score_test or score depending on the JSON using the same class?
Also I tried using nil check but since the object is initialized that is not working.
Model of Score:
class Score: Object, Decoder {
dynamic var id: String = ""
dynamic var title: String = ""
dynamic var body: String = ""
dynamic var cardOrder: Int = 0
dynamic var video: Video? = nil
override static func primaryKey() -> String? {
return "id"
}
typealias T = Score
// MARK: Decoder method
static func decode(json: JSON) -> Score {
let id = json["_id"].stringValue
let title = json["title"].stringValue
let body = json["data"].stringValue
let cardOrder = json["card_order"].intValue
var video: Video?
if (json["video"].exists()) {
video = Video.decode(json: json["video"])
}
let score = Score()
score.id = id
score.title = title
score.body = body
score.video = video
score.cardOrder = cardOrder
return score
}
}
From what I understand, score is of type [Score] so what I'd do is:
var score = json["score_test"].arrayValue.map {Score.decode(json: $0)}
if score.isEmpty {
score = json["score"].arrayValue.map {Score.decode(json: $0)}
}

Is Swift incapable of specializing generic types with generic types?

I am working with a JSON API (Reddit) using Alamofire + AlamofireObjectMapper, as well as ObjectMapper. I am working on creating the model and have a proposed solution which is not working as expected. I'll do my best to explain...
The JSON response I am receiving has a base class Thing as follows
class Thing<T where T:Mappable>: Mappable {
var id: String?
var name: String?
var kind: String?
var data: T?
required init?(_ map: Map) {}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
kind <- map["kind"]
data <- map["data"]
}
}
The data property varies depending on the type of request I am making. In this particular case, it should be of type Listing as follows.
class Listing<T where T: Mappable>: Mappable {
var before: String?
var after: String?
var modhash: String?
var children: [Thing<T>]?
required init?(_ map: Map) {}
func mapping(map: Map) {
before <- map["before"]
after <- map["after"]
modhash <- map["modhash"]
children <- map["children"]
}
}
The children property is an array of Thing objects, however with data as a different type, in this case Post
class Post: Mappable {
var author: String?
var creationDate: Int?
var name: String?
var numberOfComments: Int?
var score: Int?
var text: String?
var stickied: Int?
var subreddit: String?
var title: String?
var url: String?
var preview: Images?
required init?(_ map: Map) {}
func mapping(map: Map) {
author <- map["author"]
creationDate <- map["created_utc"]
name <- map["name"]
numberOfComments <- map["num_comments"]
score <- map["score"]
text <- map["selftext"]
stickied <- map["stickied"]
subreddit <- map["subreddit"]
title <- map["title"]
url <- map["url"]
preview <- map["preview"]
}
}
Using the AlamofireObjectMapper extension, to parse the JSON response to my custom model, we use
public func responseObject<T : Mappable>(keyPath: String? = default, completionHandler: Alamofire.Response<T, NSError> -> Void) -> Self
where the it is expecting a generic type. I will use a type defined as Response<Thing<Listing<Post>>, NSError> as follows...
RedditAPIService.requestSubReddit(subReddit: nil, withSortType: .Hot, afterPost: nil, postsAlreadyShown: nil).responseObject { (response: Response<Thing<Listing<Post>>, NSError>) -> Void in
if let value = response.result.value {
print(value.kind)
}
}
The problem I am seeing is corrupt data on the Listing object.
I am expecting only 1 value in the children object, not > 900 Zillion!
I know I can subclass Thing and Listing to use specific types, but I also am curious why this does not work as expected. Any ideas? Thanks!