Top of the page
Close

This is a red error

This is a red error

This is a red error

How to parse JSON files on iPhone in Objective-C into NSArray and NSDictionary

Ondrej Rafaj on 2009.12.16 18:45:02

Hi, today I want to show you how to parse JSON files into the NSArray and NSDictionary and how to use those files and objects. This article should be a really good starting point for you guys who already knows the basics of work with Interface Builder and XCode.

In this project we will be using very useful set of libraries from the json-framework project which is an open-source project available for free on the internet.

Please download source files for JSON framework library project here:

http://code.google.com/p/json-framework/

or you can download and open the example projech available on this page and copy those source files from this project. Files are located Classes/JSON/ folder in this example.

Don't even try to use the framework which is located in the original project on Google Code as any thirdparty framework is not permitted on iPhone platform.

Ok, let's assume you've created a new for example Navigation or Window based project, and you have the neccessary libraries already included in it. Try to build the project (you can use the Cmd + B shortcut) to see that everything is allright and we can move on to the more interesting part.

Now you have to declare IBOutlet for your textview (UITextView) and your buttons (UIButton) IBAction that will carry all the functionality after pressing the button.

IBOutlet UITextView *logWindow;

And

// our text field window
@property (nonatomic, retain) IBOutlet UITextView *logWindow;

// action we'll use after pressing the button to start parsing the file
- (IBAction)startParser:(UIButton *)sender;

the entire source code for your controller's header file (.h) is here:

//
//  TutorialProjectViewController.h
//  TutorialProject
//
//  Created by Ondrej Rafaj on 5.8.09.
//  Copyright Home 2009. All rights reserved.
//

#import 
#import "SBJSON.h"

@interface TutorialProjectViewController : UIViewController {
	
	// ------ Tutorial code starts here ------
	
	// our text field window
	IBOutlet UITextView *logWindow;
	
	// ------ Tutorial code ends here ------
	
	
}

// ------ Tutorial code starts here ------

// our text field window
@property (nonatomic, retain) IBOutlet UITextView *logWindow;

// action we'll use after pressing the button to start parsing the file
- (IBAction)startParser:(UIButton *)sender;

// ------ Tutorial code ends here ------

// This function is for button which takes you to the xprogress.com website
- (IBAction) runXprogressComButton: (id) sender;

@end

Now we have to edit the .m file. First synthesize the logWindow variable:

@synthesize logWindow;

and than, don't forget to release this variable in the dealloc function:

[logWindow release];

Start the IBAction for your button which is called startParser:

- (IBAction)startParser:(UIButton *)sender {
	
}

And start coding :)

You have to find your file in the application bundle (I'll show you how to load this file properly from the internet next time, today you have to inlude the json file into your resources folder), I assume that the json file is called data.json. We will be using just some random jsom file I found on the internet, content looks like that:

{
    "menu": {
        "header": "xProgress SVG Viewer",
        "items": [
            {
                "id": "Open"
            },
            {
                "id": "OpenNew",
                "label": "Open New"
            },
            null,
            {
                "id": "ZoomIn",
                "label": "Zoom In"
            },
            {
                "id": "ZoomOut",
                "label": "Zoom Out"
            },
            {
                "id": "OriginalView",
                "label": "Original View"
            },
            null,
            {
                "id": "Quality"
            },
            {
                "id": "Pause"
            },
            {
                "id": "Mute"
            },
            null,
            {
                "id": "Find",
                "label": "Find..."
            },
            {
                "id": "FindAgain",
                "label": "Find Again"
            },
            {
                "id": "Copy"
            },
            {
                "id": "CopyAgain",
                "label": "Copy Again"
            },
            {
                "id": "CopySVG",
                "label": "Copy SVG"
            },
            {
                "id": "ViewSVG",
                "label": "View SVG"
            },
            {
                "id": "ViewSource",
                "label": "View Source"
            },
            {
                "id": "SaveAs",
                "label": "Save As"
            },
            null,
            {
                "id": "Help"
            },
            {
                "id": "About",
                "label": "About xProgress CVG Viewer..."
            } 
        ]
    }
}

You can create new file by right-clicking the Resources folder and selecting Add -> New file -> Other (Under Mac OS X) -> Empty file. The XML equivalent will look like that:

<menu>
    <header> xProgress Viewer</header>
    <item action="Open" id="Open">Open</item>
    <item action="OpenNew" id="OpenNew">Open New</item>
    <separator/>
    <item action="ZoomIn" id="ZoomIn">Zoom In</item>
    <item action="ZoomOut" id="ZoomOut">Zoom Out</item>
    <item action="OriginalView" id="OriginalView">Original View</item>
    <separator/>
    <item action="Quality" id="Quality">Quality</item>
    <item action="Pause" id="Pause">Pause</item>
    <item action="Mute" id="Mute">Mute</item>
    <separator/>
    <item action="Find" id="Find">Find...</item>
    <item action="FindAgain" id="FindAgain">Find Again</item>
    <item action="Copy" id="Copy">Copy</item>
    <item action="CopyAgain" id="CopyAgain">Copy Again</item>
    <item action="CopySVG" id="CopySVG">Copy SVG</item>
    <item action="ViewSVG" id="ViewSVG">View SVG</item>
    <item action="ViewSource" id="ViewSource">View Source</item>
    <item action="SaveAs" id="SaveAs">Save As</item>
    <separator/>
    <item action="Help" id="Help">Help</item>
    <item action="About" id="About">About xProgress Viewer...</item>
</menu>

Ok, now we are going to get the path to the file data.json:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"];

Than you have to load contents of this file:

NSString *fileContent = [[NSString alloc] initWithContentsOfFile:filePath];

Allocate new instance of your json parser:

SBJSON *parser = [[SBJSON alloc] init];

And parse the first level of the file:

NSDictionary *data = (NSDictionary *) [parser objectWithString:fileContent error:nil];

// getting the data from inside of "menu"
NSDictionary *menu = (NSDictionary *) [data objectForKey:@"menu"];

NSDictionary is something like an array, which has numeric keys but you can use for example strings as a key.

This new NSDictionary is taken from the data dictionary by selecting the only key on this level, which is menu [data objectForKey:@"menu"].

Now we can get some more data , like the content of header as a string (NSString):

NSString *header = (NSString *) [menu objectForKey:@"header"];

And an array that is in the json file enclosed into the square brackets.

NSArray *items = (NSArray *) [menu objectForKey:@"items"];

Now you want to go trough the array and do something with the data init. You can do something like that:

for (id *item in items) {
		
		// if the item is NSDictionary (in this case ... different json file will probably have a different class)
		NSLog(@"One item: %@", [NSString stringWithFormat:@"Item -> %@", (NSDictionary *) item]);
		
}

On the end of the function don't forget to release the json data parser:

[parser release];

This file in the example is slightly different as I've used a separated function to add results into the UITextView we've created earlier.

This function is not doing anything else than adding new records on the top of the logWindow:

- (void)addRowToLogWindow:(id)data {
	
	logWindow.text = [NSString stringWithFormat:@"Adding data: %@

%@", data, logWindow.text]; // adding new row + 2x new line "
"
	
}

Code for the entire .m file is here:

//
//  TutorialProjectViewController.m
//  TutorialProject
//
//  Created by Ondrej Rafaj on 5.8.09.
//  Copyright Home 2009. All rights reserved.
//

#import "TutorialProjectViewController.h"

@implementation TutorialProjectViewController

// ------ Tutorial code starts here ------

@synthesize logWindow;


// function for adding an 
- (void)addRowToLogWindow:(id)data {
	
	logWindow.text = [NSString stringWithFormat:@"Adding data: %@

%@", data, logWindow.text]; // adding new row + 2x new line "
"
	
}

- (IBAction)startParser:(UIButton *)sender {
	
	// getting path to the file
	NSString *filePath = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"];
	
	NSLog(@"Path: %@", filePath);
		
	// reading content of the file, don't use the [NSString stringWithContentsOfFile:@"/Your/Filepath/"]; as this is deprecated since iPhone SDK 3.0
	NSString *fileContent = [[NSString alloc] initWithContentsOfFile:filePath];
	
	//NSLog(@"File content: %@", fileContent);
	
	// creating new parser
	SBJSON *parser = [[SBJSON alloc] init];
	
	// parsing the first level
	NSDictionary *data = (NSDictionary *) [parser objectWithString:fileContent error:nil];
	
	// getting the data from inside of "menu"
	NSDictionary *menu = (NSDictionary *) [data objectForKey:@"menu"];
	
	// getting the first value which is stored in the header
	NSString *header = (NSString *) [menu objectForKey:@"header"];
	[self addRowToLogWindow:[NSString stringWithFormat:@"Header -> %@", header]];
	
	// parsing all the items in to the NSArray
	NSArray *items = (NSArray *) [menu objectForKey:@"items"];
	
	// reading all the items in the array one by one
	for (id *item in items) {
		
		// if the item is NSDictionary (in this case ... different json file will probably have a different class)
		[self addRowToLogWindow:[NSString stringWithFormat:@"Item -> %@", (NSDictionary *) item]];
		
	}
	for (id *item in items) {
		
		// if the item is NSDictionary (in this case ... different json file will probably have a different class)
		NSLog(@"One item: %@", [NSString stringWithFormat:@"Item -> %@", (NSDictionary *) item]);
		
	}
	
	// releasing parser
	[parser release];
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	
	// removing the default data from our UITextView
	logWindow.text = @"";
	
    [super viewDidLoad];
	
}



// ------ Tutorial code ends here ------


/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }
    return self;
}
*/

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/





/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
	
	// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
}

// This function is for button which takes you to the xprogress.com website
- (IBAction) runXprogressComButton: (id) sender {
	NSURL *url = [[[NSURL alloc] initWithString: @"http://www.xprogress.com/"] autorelease];
	[[UIApplication sharedApplication] openURL:url];
}


- (void)dealloc {
    
	// ------ Tutorial code starts here ------
	
	[logWindow release];
	
	// ------ Tutorial code ends here ------
	
	[super dealloc];
}

@end

Now you have to connect your button and textview in Interface Builder to the IBAction for the button and logWindow outlet to the UITextView.

Now you can try to run the application and see if the data are parsed properly into the logWindow.

Hope that everything works for you. If not, feel free to leave me a comment under this article and I'll try to answer promptly. If you have any problems with the code on this website please do not use the contact form on this page as your question may be useful to someone else as well.

Thanks for your time, follow us on twitter (http://twitter.com/xprogresscom/) and come back soon for another tutorial :))

See u soon,

Ondrej

Ondrej Rafaj

Ondrej Rafaj

Technical director @ Fuerte International

Back to top Comment

Comments ... Why not to get involved!
HTML Comment Box is loading comments...

Old comments:

  • tommy huang

    tommy huang

    from 221.181.**.** @   

    iphone engineer

  • Ondrej

    Ondrej

    from 88.101.**.** @   

    goot to know tommy?! :)

  • Joe

    Joe

    from 69.113.**.** @   

    Howdy there, I enjoy the tutorial - have gotten it to compile and run without errors (only warning is that there is an unused variable 'header' which I believe may have something to do with my issue).

    Anyway, after a Build & Run, I click the button to run the startParser method, however there seems to be nothing parsed. I'll check for typos again in my data.json file - but I tried throwing in an UIAlertView into the for(id *items in item) { [self addRowToLogWindow ... ]; } and it never gets shown.

    Do you have any idea why perhaps it wouldn't parse anything? Or if there were a way for me to check if it has parsed certain parts one step at a time.

    Really cool, simple and I love how well you explain this tutorial.

    joe

  • Joe

    Joe

    from 216.156.**.** @   

    Thanks for the NSLog() tip - I've been trying to output filePath and fileContent to see if it can even see/read the file.

    However, using the syntax (with filePath for example):

    NSLog(@"File Path: %@", filePath);

    returns

    File Path: (null)

    I guess I'm not totally sure about what I can do with what types - I've tried various ways to output it but constantly get (null). You got any hints on how I could output it?

  • Ondrej

    Ondrej

    from 94.194.**.** @   

    The problem is (I guess) that you are not getting ANY data to your "filePath" variable ... beauty of NSLog is that it can print almost any type of variable even when using %@ ... for exampe NSArray or NSDictionary ... and I guess that you were printing data that could not been in because your path to the file is empty. Try to parse some really basic JSON without reading the data from file just do something like that:

    NSString *json_string = @"{ "var1": "value 1" , "var2": "value 2" }";

    if you'll do that you shoud get NSDictionary back from the parser ...

    anyway, to solve your NULL path problem, I'll need to see the line which is supposed to return the path. if you are using the one from tutorial try that:

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"data.json" ofType:@""];

    Hope this helps ... Ondrej

  • Joe

    Joe

    from 216.156.**.** @   

    I'll give it a shot with your suggestion - but I tried to NSLog() the filePath with an .xml file instead:

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"xml"];

    and it output perfectly. Is there something wrong when having four characters as the ofType: (I assume not)? I also tried using pathForResource:@"data.json" ofType:@"" and no luck either.

    I'll try parsing some basic JSON first as you suggested.

    Awesome captcha by the way

  • Joe

    Joe

    from 216.156.**.** @   

    Alright, here's the filePath return that's not getting the path to the json file:
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"];

    And I attempted to create a hardcoded JSON string:

    NSString *fileContent = @"{ 'var1' : 'value1', 'var2' : 'value2', 'var3' : 'value3' }";

    However, after initializing the parser, and creating data - I tried to NSLog() data, and I still keep getting (null)
    SBJSON *parser = [[SBJSON alloc] init];
    NSDictionary *data = (NSDictionary *) [parser objectWithString:fileContent error:nil];
    NSLog(@"data Content: %@", data);

    I even tried:
    NSDictionary *data = [fileContent JSONValue];

    however, it gives me an error in the log of:
    2010-03-30 12:17:45.833 HelloJSONFramework[5599:207] -JSONValue failed. Error trace is: (
    Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x4610280 "Object key string expected"

    I've tried both #import "JSON.h" and #import "SBJSON.h" in case that may fix something, but still nothing. Thanks for any help you can provide

  • Ondrej

    Ondrej

    from 94.194.**.** @   

    To be honest I also had this problem once before and I solved that by importing this file into the project from outside ... I don't know why it wouldn't pick up the file I've created directly in XCode (Other -> empty file) ... if you are getting the xml file, that I think it will be the same weird problem as I had ... anyway, there is no point loading JSON file from the main bundle as you can have everything in the plist which is default iPhone storage for variables and you can set all types of arrays, strings and dictionaries in it.

    if you want to use json then try to parse something from outside ... for example this file:

    http://www.xprogress.com/appDefinitions/DomainCheck/domain-list.json

    you can get it like that:

    + (NSString *)stringWithContentsOfURLInUtf8:(NSString *)targetUrl {
    NSURL *location = [NSURL URLWithString:[targetUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    NSError *error = nil;
    NSString *content = [NSString stringWithContentsOfURL:location encoding:NSUTF8StringEncoding error:&error];
    if (error) NSLog(@"Something went wrong Error: %d; Message: %@", [error code], [error localizedDescription]);
    return content;
    }

    NSString *json_string = [self stringWithContentsOfURLInUtf8:@"http://www.exaple.com/"];

    btw. that json file with domain names will return NSArray and items in it are NSDictionaries

    :)

  • Joe

    Joe

    from 216.156.**.** @   

    Thanks so much!

    I'll check that out and post my progress!

  • Joe

    Joe

    from 216.156.**.** @   

    Your first plan worked! Wow, what a crazy quirk, but now I can read and parse the JSON file.

    And also I got the second example to work, thanks so very much!

    I may poke back here if I have anymore questions related to this example, and any others I check out.

Back to top

IE SUCKS, DON'T USE IT :) Digg this page