I am using the Mantle framework in iOS for a simple JSON structure that looks like this:
{
"posts":
[
{
"postId": "123",
"title": "Travel plans",
"location": "Europe"
},
{
"postId": "456",
"title": "Vacation Photos",
"location": "Asia"
}
],
"updates": [
{
"friendId": "ABC123"
}
]
}
Essentially I am only interested in the "posts" key and wish to completely ignore the "updates" key. Additionally within the "posts" array I wish to completely ignore the "location" key. Here is how I set up my Mantle Models:
#interface MantlePost: MTLModel <MTLJSONSerializing>
#property (nonatomic, strong) NSString *postId;
#property (nonatomic, strong) NSString *title;
#end
#implementation MantlePost
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"postId": #"postId",
#"title": #"title",
};
}
#end
And here is my MantlePosts model:
#interface MantlePosts: MTLModel<MTLJSONSerializing>
#property (nonatomic, strong) NSArray<MantlePost *> *posts;
#end
#implementation MantlePosts
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"posts": #"posts"
};
}
+ (NSValueTransformer *)listOfPosts {
return [MTLJSONAdapter arrayTransformerWithModelClass:MantlePost.class];
}
#end
Finally, here is how I load my JSON up to be converted:
- (NSDictionary *)loadJSONFromFile {
NSString *jsonPath = [[NSBundle mainBundle] pathForResource:#"parse-response" ofType:#"json"];
NSError *error = nil;
NSData *jsonData = [[NSString stringWithContentsOfFile:jsonPath usedEncoding:nil error:&error] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:&error];
return jsonDict;
}
NSError = nil;
NSDictionary *jsonData = [self loadJSONFromFile];
MantlePosts *posts = (MantlePosts *)[MTLJSONAdapter modelOfClass:MantlePosts.class fromJSONDictionary:jsonData error:&error];
The problem is, my descendent array of MantlePosts contains all 3 properties postId, title, and location, when I explicitly mapped only postId and title. The "updates" array is ignored which is what I wanted but I've been stuck being able to ignore certain keys in the descendent arrays. Any help on this would be appreciated.
Here is an example of what I receive when i po the response in the console.
(lldb) po posts
<MantlePosts: 0x6000000153c0> {
posts = (
{
location = Europe;
postId = 123;
title = "Travel plans";
},
{
location = Asia;
postId = 456;
title = "Vacation Photos";
}
);
}
(lldb)
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am using objective-c and trying to deserialize JSON into an array of a custom class called Blog. So the code below should generate three objects and add them to an array of objects. I've looked at this tutorial but still ran into issues.
The JSON structure looks like this:
{
"-KGN0p1I4YFI2YNOcbv3" : {
"BlogDomain" : "blg1",
"BlogName" : "n1"
},
"-KGN198bzC54opL47vUl" : {
"BlogDomain" : "blg2",
"BlogName" : "n2"
},
"-KGN66aqkhIxBAKTcFCx" : {
"BlogDomain" : "blg3",
"BlogName" : "n3"
}
}
any help would be appreciated.
Please try out the below code:
NSString* path = [[NSBundle mainBundle] pathForResource:#"JSON" ofType:#"json"];
NSString* jsonString = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *object = [NSJSONSerialization
JSONObjectWithData:jsonData
options:0
error:&error];
if(! error) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (NSString *dictionaryKey in object) {
Blog *oBlog = [[Blog alloc] init];
oBlog.blogDomain = [[object valueForKey:dictionaryKey] objectForKey:#"BlogDomain"];
oBlog.blogName = [[object valueForKey:dictionaryKey] objectForKey:#"BlogName"];
[array addObject:oBlog];
}
} else {
NSLog(#"Error in parsing JSON");
}
//Blog.h
#import <Foundation/Foundation.h>
#interface Blog : NSObject
#property (nonatomic, strong) NSString *blogDomain;
#property (nonatomic, strong) NSString *blogName;
#end
//Blog.m
#import "Blog.h"
#implementation Blog
#end
//JSON.json
{
"-KGN0p1I4YFI2YNOcbv3" : {
"BlogDomain" : "blg1",
"BlogName" : "n1"
},
"-KGN198bzC54opL47vUl" : {
"BlogDomain" : "blg2",
"BlogName" : "n2"
},
"-KGN66aqkhIxBAKTcFCx" : {
"BlogDomain" : "blg3",
"BlogName" : "n3"
}
}
The following codes parsing JSON don't work any more for me after I update Mantle to 2.0. They can work fine on an older Mantle version( I don't remember the correct version number. What I know is I downloaded it in Nov of 2013.)
Here is the JSON content:
{
date = "2015-05-21";
error = 0;
results = (
{
currentCity = "beijing";
index = (
{
des = "desc1";
tipt = "tipt1";
title = "title1";
zs = "zs1";
},
{
des = "desc2";
tipt = "tipt2";
title = "title2";
zs = "zs2";
},
{
des = "desc3";
tipt = "tipt3";
title = "title3";
zs = "zs3";
}
);
}
);
status = success;
}
The Model I defined:
// .h
#import "MTLModel.h"
#import "Mantle.h"
#interface BaiduWeatherResults : MTLModel<MTLJSONSerializing>
#property (nonatomic, strong) NSNumber *error;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSString *date;
#property (nonatomic, strong) NSString *currentCity;
#end
// .m
#import "BaiduWeatherResults.h"
#implementation BaiduWeatherResults
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"error" : #"error",
#"status" : #"status",
#"date" : #"date",
#"currentCity" : #"results.currentCity",
};
}
+ (NSValueTransformer *) currentCityJSONTransformer
{
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSArray *values) {
return [values firstObject];
} reverseBlock:^(NSString *str) {
return #[str];
}];
}
Parse JSON to Model
id results =[MTLJSONAdapter modelOfClass:[BaiduWeatherResults class]
fromJSONDictionary:responseObject
error:nil];
NSLog(#"results:%#", results);
My Question:
The codes can work on an older Mantle. On the Mantle 2.0, the parse failed once I added #"currentCity" : #"results.currentCity" into the dictionary returned by JSONKeyPathsByPropertyKey . Anyone know what I missed for the parsing?
BTW, the currentCityJSONTransformer did call when the parse began. But the transformer is never used, because the line "return [values firstObject];" is never executed.
Thanks in advance.
Try this -
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"error" : #"error",
#"status" : #"status",
#"date" : #"date",
#"currentCity" : #"results",
};
}
+ (NSValueTransformer *) currentCityJSONTransformer
{
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSArray *values) {
NSDictionary *cityInfo = [values firstObject];
return cityInfo[#"currentCity"];
} reverseBlock:^(NSString *str) {
return #[#{#"currentCity" : str}];
}];
}
Since results is an array of dictionaries, you can't access currentCity via dot syntax in JSONKeyPathsByPropertyKey. Instead the currentCityJSONTransformer finds the first dictionary in the results array and returns its value for currentCity. You might want to add type-checking and define the #"currentCity" key in a single place.
i need to display a Table View containing information from web service response i do no where iam doing wrong here my sample code
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
recordResults =NO;
appDelegate.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
appDelegate.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
appDelegate.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
appDelegate.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
}
i need to parse 4 values from plan_history like "rate","description","validity","plan type"
when i run my app i getting only one set of value in Table view . i.e my json string contains more than 20 records containing rate,description,validity and plan type
can u show me how to loop my json value and display all my records in Table View
You should eliminate those calls to allValues and valueForKey, as repeatedly calling those methods is very inefficient ways to tackle JSON parsing.
In one of your comments, you said that your JSON looked like:
{
"plan_history": [
{
"rate": "₹1000",
"description": "FullTalktimeTopupRs.1000FullTalktime",
"validity": "Validity: 0\r",
"plantype": "FullTalkTime"
},
{
"rate": "₹508",
"description": "FullTalktimeTopupRs.558morethanFullTalktime",
"validity": "Validity: 2\r",
"plantype": "FullTalkTime"
}
]
}
(I wonder if there was something before this plan_history entry given your allValues reference, but unless you tell us otherwise, I'll assume this is what the original JSON looked like.)
If so, to parse it you would do:
NSMutableArray *results = [NSMutableArray array];
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSArray *planHistory = json[#"plan_history"];
for (NSDictionary *planHistoryEntry in planHistory) {
NSString *rateString = planHistoryEntry[#"rate"];
NSString *description = planHistoryEntry[#"description"];
NSString *validity = planHistoryEntry[#"validity"];
NSString *planType = planHistoryEntry[#"plantype"];
// now do whatever you want with these four values.
// for example, I'd generally create a custom object I defined elsewhere for these four values and add to results, e.g.
[results addObject:[PlanHistoryEntry planHistoryEntryWithRate:rateString
description:description
validity:validity
planType:planType]];
}
// now do something with results, e.g. store it in some property in `appDelegate`, etc.
Where, PlanHistoryEntry might be defined like so:
#interface PlanHistoryEntry : NSObject
#property (nonatomic, copy) NSString *rateString;
#property (nonatomic, copy) NSString *planDescription; // note, do not use `description` for property name
#property (nonatomic, copy) NSString *validity;
#property (nonatomic, copy) NSString *planType;
+ (instancetype) planHistoryEntryWithRate:(NSString *)rateString
planDescription:(NSString *)planDescription
validity:(NSString *)validity
planType:(NSString *)planType;
#end
#implementation PlanHistoryEntry
+ (instancetype) planHistoryEntryWithRate:(NSString *)rateString
planDescription:(NSString *)planDescription
validity:(NSString *)validity
planType:(NSString *)planType
{
PlanHistoryEntry *entry = [[self alloc] init];
entry.rateString = rateString;
entry.planDescription = planDescription;
entry.validity = validity;
entry.planType = planType;
return entry;
}
#end
But I don't want you to get lost in the minutiae of this answer (because given the ambiguity of the question, I may have gotten some details wrong). The key point is that you should not be using allValues or valueForKey. Just navigate the JSON structure more directly as illustrated above.
Try this,
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *rateArray = [[json objectForKey:#"plan_history"] objectForKey:#"rate"];
NSArray * descriptionArray = [[json objectForKey:#"plan_history"] objectForKey:#"description"];
NSArray * validityArray = [[json objectForKey:#"plan_history"] objectForKey:#"validity"];
NSArray * plantypeArray = [[json objectForKey:#"plan_history"] objectForKey:#"plantype"];
and use rateArray, descriptionArray etc.
You can create a class storing your data as follows:
Something like:
planClass.h
#property(nonatomic, strong) NSString * rateString;
#property(nonatomic, strong) NSString * descriptionString;
#property(nonatomic, strong) NSString * validityString;
#property(nonatomic, strong) NSString * plantypeString;
plan.m
//#synthesize the properties of .h
Now in your .m file where you want to parse the data you can do something like:
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
planClass *pc = [[planClass alloc]init];
recordResults =NO;
pc.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
pc.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
pc.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
pc.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
[appDelegate.arrayPlan addObject:pc];
}
NSLog(#"appDelegate.arrayPlan >> %#",appDelegate.arrayPlan); // you'll get array of planClass objects
You can now access the arrayPlan declared in appDelegate as follows:
for(id *obj in arrayPlan)
{
planClass *pc = (planClass *)obj;
NSLog("rate: %#",[pc valueForKey:#"rateString"]);
NSLog("descriptionString: %#",[pc valueForKey:#"descriptionString"]);
NSLog("validityString: %#",[pc valueForKey:#"validityString"]);
NSLog("plantypeString: %#",[pc valueForKey:#"plantypeString"]);
}
Hope this helps.
you need to store that value in fatalist control means in NSMutable array like this.
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSMutableArray *arrayHistory = [[NSMutableArray alloc]init];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
recordResults =NO;
appDelegate.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
appDelegate.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
appDelegate.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
appDelegate.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
[arrayHistory addObject:appDelegate.rateString];
[arrayHistory addObject:appDelegate.descriptionString];
[arrayHistory addObject:appDelegate.validityString];
[arrayHistory addObject:appDelegate.plantypeString];
}
Now use
arrayHistory
to load data in table view
Hey I'm new to iPhone and I have been trying to parse the below JSON for displaying different types of Survey using my below code. I have two tables, in first table i want to display all the "Surveys_title" text value, and once user will tap on any survey title row then it should display their particular question and question ID in my second table. Like i have two questions for "Survey1" and three questions for "Survey2". Using my code, i am able to display all survey titles in my first table but i am getting problem that how to store array of objects for all the survey types individually. here, I have created one custom class "Survey". Thank you for any help you can give me.
JSON :
{
"Surveys": [
{
"Surveys_title": "Survey1",
"Questions": [
{
"event_sq_qns_id": 1,
"questions": "What is your primary job title/focus?"
},
{
"event_sq_qns_id": 2,
"questions": "Effectiveness of the speakers?"
}
]
},
{
"Surveys_title": "Survey2",
"Questions": [
{
"event_sq_qns_id": 3,
"questions": "What is this?"
},
{
"event_sq_qns_id": 4,
"questions": "Who are you?"
},
{
"event_sq_qns_id": 5,
"questions": "what is your name?"
}
]
},
{
"Surveys_title": "Survey3",
"Questions": [
{
"event_sq_qns_id": 6,
"questions": "What is your primary job?"
},
{
"event_sq_qns_id": 7,
"questions": "Effectiveness of the speakers?"
}
]
}
]
}
here is my code :
#import <Foundation/Foundation.h>
#interface Surveys : NSObject
#property (nonatomic, retain) NSString *surveys_question_id;
#property (nonatomic, retain) NSString *questions;
#end
- (void) fetchingSurveyQuestionsFromServer
{
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSDictionary *results;
#try {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"survey" ofType:#"json"];
NSData *responseData = [NSData dataWithContentsOfFile:filePath];
//parse the json data
NSError *error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
results= [json objectForKey:#"Surveys"];
}
#catch (NSException *exception) {
NSLog(#"Exception in %s %#",__FUNCTION__,exception);
}
dispatch_async (dispatch_get_main_queue (),
^{
arraySurveys = [[NSMutableArray alloc] init];
arraySurveys_type = [[NSMutableArray alloc] init];
NSString *surveys_title_name;
for (NSDictionary *dict in results) {
NSDictionary *questionDict = dict[#"Questions"];
surveys_title_name = dict[#"Surveys_title"];
NSLog(#"Questions dictionary = %#", questionDict);
NSLog(#"Survey type is = %#", surveys_title_name);
for (NSDictionary *dict1 in questionDict) {
Surveys *surveys = [[Surveys alloc] init];
surveys.surveys_question_id = [dict1 objectForKey:#"event_sq_qns_id"];
surveys.questions = [dict1 objectForKey:#"survey_questions"];
[arraySurveys addObject:surveys];
}
[arraySurveys_type addObject:surveys_title_name];
}
[MBProgressHUD hideHUDForView:self.view animated:YES];
[tblSurveys reloadData];
});
});
}
Using above code, all the questions are adding directly to the arraySurveys. please help me how can i differentiate according to the Surveys title.
Thanks.
Use like this ....
SBJSON *json = [[SBJSON alloc] init];
NSMutableDictionary *jsonObject = [json objectWithString:response ];
NSMutableArray *Surveys=[jsonObject valueForKey:#"Surveys"];
NSMutableArray * Surveys_title =[[NSMutableArray alloc]init];
NSMutableArray * Questions =[[NSMutableArray alloc]init];
for (NSDictionary *dictnory in Surveys) {
[Surveys_title addObject:[dictnory objectForKey:#"Surveys_title"]];
[Questions addObject:[dictnory objectForKey:#"Questions"]];
}
I want to write the method which should create for writing a plist file. I got the example code in the Web but can not understand what is wrong with it. First of all, when I try to call this method - I get a message in log:
2013-03-28 15:33:47.953 ECom[6680:c07] Property list invalid for format: 100 (property lists cannot contain NULL)
2013-03-28 15:33:47.954 ECom[6680:c07] An error has occures <ECOMDataController: 0x714e0d0>
Than why does this line return (null)?
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
and the last question - how to remove the warning message for the same line?
Incompatible pointer to integer conversion sending 'void *' to parameter of type 'NSPropertyListWriteOptions' (aka 'insigned int')
h-file
#import <Foundation/Foundation.h>
#interface ECOMDataController : NSObject
{
CFStringRef trees[3];
CFArrayRef treeArray;
CFDataRef xmlValues;
BOOL fileStatus;
CFURLRef fileURL;
SInt32 errNbr;
CFPropertyListRef plist;
CFStringRef errStr;
}
#property(nonatomic, retain) NSMutableDictionary * rootElement;
#property(nonatomic, retain) NSMutableDictionary * continentElement;
#property(nonatomic, strong) NSString * name;
#property(nonatomic, strong) NSString * country;
#property(nonatomic, strong) NSArray * elementList;
#property(nonatomic, strong) id plistData;
#property(nonatomic, strong) NSString * plistPath;
#property(nonatomic, strong) NSData * data;
#property(nonatomic, strong) id filePathObj;
-(void)CreateAppPlist;
#end
m-file
#import "ECOMDataController.h"
#implementation ECOMDataController
#synthesize rootElement, continentElement, country, name, elementList, plistData, data, plistPath;
- (void)CreateAppPlist {
// Get path of data.plist file to be created
plistPath = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
// Create the data structure
rootElement = [NSMutableDictionary dictionaryWithCapacity:3];
NSError *err;
name = #"North America";
country = #"United States";
continentElement = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:name, country, nil] forKeys:[NSArray arrayWithObjects:#"Name", #"Country", nil]];
[rootElement setObject:continentElement forKey:#""];
//Create plist file and serialize XML
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
if(data)
{
[data writeToFile:plistPath atomically:YES];
} else {
NSLog(#"An error has occures %#", err);
}
NSLog(#"%# %# %#", plistPath, rootElement, data);
}
#end
It seems that you are serializing the wrong element, replace plistData by rootElement (and nil by 0) in
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];