jsonmodel deserialize object - objective-c

I'm using jsonmodel for serializing operations. I send post to server and get json data. I deserialize the data to this object.
#import <Foundation/Foundation.h>
#import "JSONModel.h"
#interface ResultObject : JSONModel
#property (strong, nonatomic) NSObject<Optional> *Data;
#property (strong, nonatomic) NSString *ResultCode;
#property (strong, nonatomic) NSString *ResultMessage;
#property (strong, nonatomic) NSObject<Optional> *Exception;
#end
I can get simple data. Like a boolean value or a string. But when i try to cast Data to my custom object. the data were corrupted.
I'm using this code to cast.
ResultObject *resultObject = [[ResultObject alloc]initWithString:result error:&error];
NSString *returnAnswer = [NSString stringWithFormat:#"%#",resultObject.Data];
LanguagePack *pack =[[LanguagePack alloc]initWithString:returnAnswer usingEncoding:NSASCIIStringEncoding error:&error];
the colons(:) change to equals(=)
and the comas(,) change to semicolons(;) in the returnAnswer so "pack" is null. I can't deserialize the json data.
this is my LanguagePack
#interface LanguagePack : JSONModel
#property(strong,nonatomic) NSArray<LanguageString> *Data;
#end
and this is my LanguageString
#protocol LanguageString;
#interface LanguageString : JSONModel
#property (strong, nonatomic) NSString *DataKey;
#property (strong, nonatomic) NSString *DataValue;
#property (strong, nonatomic) NSString *DataDescription;
#property (strong, nonatomic) NSString *DataLanguage;
#end
My question is that how can i deserialize the json data inside the NSObject(Data) to my custom Objects?
Edit Note: when i look at ResultObject.Data it has 14 objects(as should be) but every object has an error:
expected ']'error: 1 errors parsing expression
like this.
and if i change NSObject<Optional> *Data to NSArray<LanguageString> *Data it works properly. But i need a generic type like NSObject.
My Json
{
"Data":[
{
"DataKey":"AppTemplate.CancelButton.Text",
"DataValue":"Iptal",
"DataDescription":"",
"DataLanguage":"TR"
},
{
"DataKey":"Exception.Code.07",
"DataValue":"SMS dogrulama kodu hatali ya da zaman asimina ugramis.",
"DataDescription":"SmsVerificationCodeNotVerifiedException",
"DataLanguage":"TR"
},
{
"DataKey":"Exception.Code.11",
"DataValue":"Geçersiz dil bilgisi.",
"DataDescription":"InvalidLanguageException",
"DataLanguage":"TR"
}
],
"ResultCode":"00",
"ResultMessage":"Success",
"Exception":null
}
Sorry for my english and thanks for help.

I have use JSONModel framework to find out whats going on.
Note, please use camel case notation in your project.
Model classes (only header files are important in this case):
LanguageString.h
#import "JSONModel.h"
#protocol LanguageString;
#interface LanguageString : JSONModel
#property (strong, nonatomic) NSString *DataKey;
#property (strong, nonatomic) NSString *DataValue;
#property (strong, nonatomic) NSString *DataDescription;
#property (strong, nonatomic) NSString *DataLanguage;
#end
LanguagePack.h
#import "JSONModel.h"
#import "LanguageString.h"
#interface LanguagePack : JSONModel
#property(strong,nonatomic) NSArray<LanguageString> *Data;
#end
ResultObject
#import <Foundation/Foundation.h>
#import "JSONModel.h"
#import "LanguageString.h"
#interface ResultObject : JSONModel
#property (strong, nonatomic) NSArray<LanguageString> *Data;
#property (strong, nonatomic) NSString *ResultCode;
#property (strong, nonatomic) NSString *ResultMessage;
#property (strong, nonatomic) NSObject<Optional> *Exception;
#end
and then run:
NSString *json = #"{\"Data\":[{\"DataKey\":\"AppTemplate.CancelButton.Text\",\"DataValue\":\"Iptal\",\"DataDescription\":\"\",\"DataLanguage\":\"TR\"},{\"DataKey\":\"Exception.Code.7\",\"DataValue\":\"SMS dogrulama kodu hatali ya da zaman asimina ugramis.\",\"DataDescription\":\"SmsVerificationCodeNotVerifiedException\",\"DataLanguage\":\"TR\"},{\"DataKey\":\"Exception.Code.11\",\"DataValue\":\"Geçersiz dil bilgisi.\",\"DataDescription\":\"InvalidLanguageException\",\"DataLanguage\":\"TR\"}],\"ResultCode\":\"00\",\"ResultMessage\":\"Success\",\"Exception\":null}";
ResultObject *ro = [[ResultObject alloc] initWithString:json error:nil];
NSLog(#"ResultCode=%#, ResultMessage=%#", ro.ResultCode, ro.ResultMessage);
for (LanguageString *ls in ro.Data) {
NSLog(#"\n-----\nDataKey=%#\nDataValue=%#\nDataDescription=%#\nDataLanguage=%#\n-----", ls.DataKey, ls.DataValue, ls.DataDescription, ls.DataLanguage);
}
RESULT:
2014-01-24 14:46:31.050 Test[1420:70b] ResultCode=00, ResultMessage=Success
2014-01-24 14:46:31.052 Test[1420:70b]
-----
DataKey=AppTemplate.CancelButton.Text
DataValue=Iptal
DataDescription=
DataLanguage=TR
-----
2014-01-24 14:46:31.052 Test[1420:70b]
-----
DataKey=Exception.Code.7
DataValue=SMS dogrulama kodu hatali ya da zaman asimina ugramis.
DataDescription=SmsVerificationCodeNotVerifiedException
DataLanguage=TR
-----
2014-01-24 14:46:31.053 Test[1420:70b]
-----
DataKey=Exception.Code.11
DataValue=Geçersiz dil bilgisi.
DataDescription=InvalidLanguageException
DataLanguage=TR
-----
I hope it is what you are expecting, a ResultObject must define the type of the array, thats the only difference:
#property (strong, nonatomic) NSArray<LanguageString> *Data;

The "Data" key in your JSON feed IS an array. Therefore your "Data" property also needs to be an array instead of NSObject.
But I guess you already knew that since you noted that it works if you do that

Provide the JSON structure of your custom Objects - I can't give you clear answer without it.
At least, I can recommend for JSON a NSJSONSerialization
You can just start with something like:
NSData *responseData; // insert your data here
NSDictionary *response = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:nil];
NSLog(#"response :%#", response);
Note, instead of '(NSDictionary*)' you may use '(NSArray*)' - it depends from your data structure.
OK, so I'll give you some idea how to parse it using NSJOSONSerlization:
NSData* responseData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *response = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:nil];
NSLog(#"response :%#", response);
// Parse
NSString *resultCode = response[#"ResultCode"];
NSString *resultMessage = response[#"ResultMessage"];
NSArray *dataArr = response[#"Data"];
for (NSDictionary *item : dataArr) {
LanguageString *ln = [LanguageString new];
ln.dataKey = item[#"DataKey"];
ln.dataValue = item[#"DataValue"];
ln.dataDescription = item[#"DataDescription"];
ln.dataLanguage = item[#"DataLanguage"];
/* TODO: store 'ln' object in desired model */
}
Of course assuming, that dataStr is similar to:
NSString *dataStr = #"{\"Data\":[{\"DataKey\":\"AppTemplate.CancelButton.Text\",\"DataValue\":\"Iptal\",\"DataDescription\":\"\",\"DataLanguage\":\"TR\"},{\"DataKey\":\"Exception.Code.7\",\"DataValue\":\"SMS dogrulama kodu hatali ya da zaman asimina ugramis.\",\"DataDescription\":\"SmsVerificationCodeNotVerifiedException\",\"DataLanguage\":\"TR\"},{\"DataKey\":\"Exception.Code.11\",\"DataValue\":\"Geçersiz dil bilgisi.\",\"DataDescription\":\"InvalidLanguageException\",\"DataLanguage\":\"TR\"}],\"ResultCode\":\"00\",\"ResultMessage\":\"Success\",\"Exception\":null}";
Please note, this is only a hint. Also consider using such framework as ResKit

Related

Swift: "Mapper is not a type", using custom mapping class for arrays in swift and Objective C

I have an 2 objective C classes:
Class 1 scanDatabase (Scans a database and puts that into a mutable array)
Class 2 Mapper (Is a mapping class for the database scan "model")
In objective C this successfully scans the database and puts it into a mutable array. Using the mapping class I can access individual groups of elements (AlbumTitles) like so:
for (Mapper *mapper in scanResult) {
NSLog(#"%#", mapper.AlbumTitle);
}
Everything is working as it should and I can return individual elements from my array i.e as above I am only returning album titles.
I then need to use that array in Swift. I call the objective C in my Swift class and again it runs fine and creates the array. This is done with:
let scanTable = ScanTable();
let scanMapper = Mapper();
scanTable.scanTableDo();
but when I try to retrieve a particular set of items like Album title as I did in the objective C for loop above I get the error "scanMapper is not a type" (scanMapper is my swift instance of the objective C mapper class:
I tried two different ways and both have the same error:
for mapper: scanMapper in scanTable.scanResult {
print("\(mapper.AlbumTitle)")
}
for object in scanTable.scanResult as! [scanMapper] {
print("\(mapper.AlbumTitle)")
}
Can I use an objective C class as a model/mapper and not sure whether I would need to recreate it in Swift.
I will include the mapper and scanTable .h and .m code just in case it is needed, plus the bridging header:
Mapper.h:
#import <Foundation/Foundation.h>
#import <AWSDynamoDB/AWSDynamoDB.h>
#interface Mapper : AWSDynamoDBObjectModel <AWSDynamoDBModeling>
#property (nonatomic, strong) NSNumber *SongID;
#property (nonatomic, strong) NSString *Artist;
#property (nonatomic, strong) NSString *SongURL;
#property (nonatomic, strong) NSString *Location;
#property (nonatomic, strong) NSNumber *UserRatings;
#property (nonatomic, strong) NSNumber *AVGUserRating;
#property (nonatomic, strong) NSString *Category;
#property (nonatomic, strong) NSString *PictureURL;
#property (nonatomic, strong) NSNumber *SongDuration;
#property (nonatomic, strong) NSString *SongTitle;
#property (nonatomic, strong) NSNumber *AVGMusicianRating;
#property (nonatomic, strong) NSString *AlbumTitle;
#end
Mapper.m
#import <AWSDynamoDB/AWSDynamoDB.h>
#import "Mapper.h"
#implementation Mapper
+ (NSString *)dynamoDBTableName {
return #"Songs";
}
+ (NSString *)hashKeyAttribute {
return #"SongID";
}
#end
ScanTable.h:
#import <Foundation/Foundation.h>
#import <AWSDynamoDB/AWSDynamoDB.h>
#interface ScanTable : NSObject
- (void) scanTableDo;
#property (nonatomic, strong) NSMutableArray *scanResult;
#end
ScanTable.m
#import "ScanTable.h"
#import "Mapper.h"
#implementation ScanTable
- (void) scanTableDo {
AWSDynamoDBObjectMapper *dynamoDBObjectMapper = [AWSDynamoDBObjectMapper defaultDynamoDBObjectMapper];
AWSDynamoDBScanExpression *scanExpression = [AWSDynamoDBScanExpression new];
scanExpression.limit = #10;
[[dynamoDBObjectMapper scan:[Mapper class]
expression:scanExpression]
continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"The request failed. Error: [%#]", task.error);
}
if (task.exception) {
NSLog(#"The request failed. Exception: [%#]", task.exception);
}
if (task.result) {
AWSDynamoDBPaginatedOutput *paginatedOutput = task.result;
NSMutableArray *scanResult = [[NSMutableArray alloc] initWithArray:paginatedOutput.items]; //// ADDED /////
for (Mapper *mapper in scanResult) {
NSLog(#"%#", mapper.AlbumTitle);
}
}
return nil;
}];
}
#end
//EDITED ADDED BRIDGING HEADER//
MySampleApp-Bridging-Header.h:
//
// MySampleApp-Bridging-Header.h
// MySampleApp
#import "ScanTable.h"
#import "Mapper.h"
#import "Hello World.h"
Thanks for your help
The problem is just as the error explains, you're attempting to cast the items in your array to scanMapper, which is a variable holding an instance of Mapper, not the Mapper type itself. Assuming that scanTable.scanResult is an NSArray of Mappers, try this instead:
guard let scanResult = scanTable.scanResult as? [Mapper] else {
print("scanResult was not an array of mappers!")
return
}
for mapper: Mapper in scanResult {
print("\(mapper.AlbumTitle)")
}

How can I cast my NSURLSessionDownloadTask to my custom NSURLSessionDownloadTask (inheritance)?

I have created a custom NSURLSessionDownloadTask named VJSessionTask and I have just added some custom things like a type (enum) and a custom object (id):
#interface VJSessionTask : NSURLSessionDownloadTask
typedef enum types
{
LS, LSH, DL, UL, RM, TH
} type;
#property enum types type;
#property (strong, nonatomic) id customObject;
#property (strong, nonatomic) NSString *progressNotif;
#property (strong, nonatomic) NSString *doneNotif;
#property (strong, nonatomic) NSURL *tmpFile;
#end
And when I do this:
VJSessionTask *taskSession = (VJSessionTask *)[self.prioritySession downloadTaskWithRequest:listFileRequest];
// init taskSession with its type
taskSession.type = LS;
I get this error:
-[__NSCFLocalDownloadTask setType:]: unrecognized selector sent to instance 0x1556198f0
Then I come to you as I don't understand or I don't know how to do that...
Thank you in advance ;)
NSURLSessionTasks are not strictly speaking subclass-able unfortunately. This is evident in that the system can queue a data task and return a NSCFLocalDownloadTask (presumably meaning that the task will return its content from the cache).
The best way to go about doing this is to borrow on from the architectural decision of AFNetworking and have individual taskDelegates that monitor all the responses an individual task works on. Then when you want to find the data relating to a task you can query your dictionary of taskDelegates. Each task has a unique identifier that you can use to key your dictionary with.
In AFNetworking you can see the taskDelegate is defined as follows:
#interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
#property (nonatomic, weak) AFURLSessionManager *manager;
#property (nonatomic, strong) NSMutableData *mutableData;
#property (nonatomic, strong) NSProgress *progress;
#property (nonatomic, copy) NSURL *downloadFileURL;
#property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
#property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
#end
#implementation AFURLSessionManagerTaskDelegate
and subsequently retrieved as follows:
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[#(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
See this post for more info

JSONModel - Key is a number, can I get the children by offset?

How do I parse this JSON in Objective C? I've been using jsonmodel.com's code to parse.
{
"found":10958,
"start":3141,
"hits":[
{
"pid":"76493",
"title":"Beton Armu00e9",
"artist":"Raiden",
"genre":"Dubstep",
"image":"A76493_BetonArm_BetonArm.jpg",
"label":"Offkey",
"year":"2011",
"price":9.99,
"release":"Beton Armu00e9",
"type":"Album",
"tracks":{
"0":{
"name":"Barbican",
"file":"A76481_Barbican.mp3",
"tracknum":1,
"pid":"76481"
},
"1":{
"name":"Trinity",
"file":"A76482_Trinity.mp3",
"tracknum":2,
"pid":"76482"
},
"2":{
"name":"Tricorn",
"file":"A76483_Tricorn.mp3",
"tracknum":3,
"pid":"76483"
},
"3":{
"name":"Brutalist",
"file":"A76484_Brutalist.mp3",
"tracknum":4,
"pid":"76484"
},
"4":{
"name":"Trellick",
"file":"A76485_Trellick.mp3",
"tracknum":5,
"pid":"76485"
}
}
}
]
}
JSONModel expects a pointer string to declare the keys, but the keys here are numbers. This is what I need, but won't work:
#import "JSONModel.h"
#import "songParentModel.h"
#protocol albumModel #end
#interface albumModel : JSONModel
#property (strong,nonatomic) NSString *title;
#property (strong,nonatomic) NSString *image;
#property (strong,nonatomic) NSString *artist;
#property (strong,nonatomic) songParentModel *0; // THIS DOESN'T WORK (of course)
#end
I just need to get the first track, but it would be nice to know how to get them all.
My best guess for a solution would be to stop using JSONModel and parse the JSON with some other simplified method.
Create a class like this :
The header:
#import "JSONModel.h"
#protocol Track #end
#interface Track : JSONModel
#property (strong, nonatomic) NSString* name;
#property (assign, nonatomic) NSString* file;
#property (assign, nonatomic) int tracknum;
#property (strong, nonatomic) int pid;
#end
Leave the implementation as default.
now in your model add this property:
#property (strong, nonatomic) NSArray<Track>* allTracks;
And also change the implementation for +(JSONKeyMapper*)keyMapper and add the below item to your dictionary.
+(JSONKeyMapper*)keyMapper
{
return [[JSONKeyMapper alloc] initWithDictionary:#{
#"tracks":#"allTracks",
}];
}
In this way you get an array of all the tracks and you can also get all the details for each track as well.

JSONModel: can't assign fetched JSON to model

My JSON I fetch:
{"username":"example","confirmed_rewards":"5","round_estimate":"0.73605946","total_hashrate":"0","payout_history":"10","round_shares":"85",
"workers":{
"worker.1":{"alive":"0","hashrate":"0"},
"worker.2":{"alive":"0","hashrate":"0"}
}
}
My model:
#import "JSONModel.h"
#protocol LKCoinFCPoolModel #end
#interface LKCoinFCPoolModel : JSONModel
#property (strong, nonatomic) NSString* username;
#property (strong, nonatomic) NSString* confirmed_rewards;
#property (strong, nonatomic) NSString* round_estimate;
#property (strong, nonatomic) NSString* total_hashrate;
#property (strong, nonatomic) NSString* payout_history;
#property (strong, nonatomic) NSString* round_shares;
#property (strong, nonatomic) NSString<Optional> * ErrorCode;
#end
I created the following function which is to fetch a JSON structure and assign it to a model.
-(void)viewDidAppear:(BOOL)animated
{
//show loader view
[HUD showUIBlockingIndicatorWithText:#"Fetching JSON"];
//fetch the feed
LKCoinFCPoolModel* test;
test = [[LKCoinFCPoolModel alloc] initFromURLWithString:#"http://example.com/ap/key"
completion:^(LKCoinFCPoolModel *model, JSONModelError *err) {
//hide the loader view
[HUD hideUIBlockingIndicator];
//json fetched
NSLog(#"user: %#", test.username);
}];}
the problem I'm having is that instead of user: example it prints user: (null).
I'm unsure what I'm doing wrong, it's the first app I'm trying to write in xcode (I'm coming from a Python/Java background).
Your callback is passing the the parsed JSON to you in the LKCoinFCPoolModel *model. In fact I don't think that you can assign test the way you do. If you really wanted to get it into the test model, you should assign it inside the block. Remember that this block runs asynchronous after the JSON is downloaded and parsed. So test won't be valid until then.

Unable to refresh UITableView with modified data

I have a iPad app, using XCode 4.5, Storyboards, Core Data and iOS 6. I select a row, make a change to the contents of the record (which is successful), but the row doesn't change. I have tried to refresh the UITableView, but cellForRowAtIndexPath is never called. I have searched SO and Google to no avail; I don't see what's wrong. Can someone please tell me how to fix this? (with an explanation of what I'm doing wrong for the next time?)
Here is the pertinent code:
- (IBAction)btnModify:(UIButton *)sender {
//NSLog(#"btnModify clicked");
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
// find client by primary telephone number
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"aClientPrimaryPhone ==[c] %#", cvPrimaryPhone.text];
ClientInfo *clientDataFound = [ClientInfo MR_findFirstWithPredicate:predicate inContext:localContext];
if(clientDataFound) {
clientDataFound.aClientName = cvCustName.text; // now start moving the data
clientDataFound.aClientAddr1 = cvAddress1.text;
clientDataFound.aClientAddr2 = cvAddress2.text;
clientDataFound.aClientCity = cvContactCity.text;
clientDataFound.aClientPostalCode = cvPostalCode.text;
clientDataFound.aClientCellPhone = cvCellPhone.text;
clientDataFound.aClientPrimaryPhone = cvPrimaryPhone.text;
clientDataFound.aClientEMail = cvPersonalEmail.text;
clientDataFound.aClientNotes = cvNotes.text;
[localContext MR_saveNestedContexts];
[self reloadClientList];
}
}
-(void) reloadClientList {
//Init Array to hold TableView Data
tableDataArray = [NSMutableArray new];
[tableDataArray addObjectsFromArray:[ClientInfo findAll]]; // Load
[self.clientList reloadData];
}
and this is ClientInfo.m
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface ClientInfo : NSManagedObject
#property (nonatomic, retain) NSString * aClientAddr1;
#property (nonatomic, retain) NSString * aClientAddr2;
#property (nonatomic, retain) NSString * aClientCellPhone;
#property (nonatomic, retain) NSString * aClientCity;
#property (nonatomic, retain) NSString * aClientEMail;
#property (nonatomic, retain) NSData * aClientImage;
#property (nonatomic, retain) NSString * aClientName;
#property (nonatomic, retain) NSString * aClientNotes;
#property (nonatomic, retain) NSString * aClientPostalCode;
#property (nonatomic, retain) NSString * aClientPrimaryPhone;
#end
I found it... my "clientList" was NOT connected to the object... don't know how I missed that one!
There are a few reasons I can think of:
Your table view reference clientList is nil. (not connected)
Your table view's DataSource & Delegate is not set (Actually I'm not sure if it compiles when DataSource is not set)
Your table view is a subclass of UITableView and in that subclass reloadData method is overridden.