0

I am trying to a read JSON file containing contact info objects consisting of NSString types and NSMutableArrays. Currently, I am using NSData to read the whole file and then parsing through it. I have utilised Stig's example as mentioned here: SBJson4Parser Example

   SBJson4ValueBlock block = ^(id obj, BOOL *stop) {
   NSLog(@"Found: %@", @([obj isKindOfClass:[NSDictionary class]]));
    //contactsData *contact = obj;
    NSDictionary *contact = obj;
    NSLog(@"Contact: %@",contact);
   /* NSString *fName, *lName;
    fName = [contact objectForKey:@"mFirstName"];
     lName = [contact objectForKey:@"mLastName"];
    NSLog(@"First Name: %@",fName);
    NSLog(@"Last Name: %@",lName);
    */
};

SBJson4ErrorBlock eh = ^(NSError* err){
    NSLog(@"Oops: %@",error);
};
NSLog(@"Parse work");
id parser = [SBJson4Parser multiRootParserWithBlock:block
                                       errorHandler:eh];
//uint8_t buf[1024];
//unsigned int len = 0;
NSLog(@"Trying to push stream to data");
//[inputStream read:buf maxLength:len];
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSUTF8StringEncoding error:NULL];
//id data = [json da:NSUTF8StringEncoding];
SBJson4ParserStatus status = [parser parse:data];

NSLog(@"Status: %u",status);

These days people seem to have hundreds or even thousands of contacts, thanks to social networks. Will this lead to a larger memory footprint on an iOS device ? If so, how do I parse a single object from a stream ? If I have to use a delegate, an example would be greatly appreciated.

Please note that I am new to the world of iOS development as well as Objective-C.

The structure of the json file in question:
{
  "mAddresses": [
  ],
  "mContactPhoto": "",
  "mDisplayName": ",tarun,,,,israni,,", 
  "mPhoneNumberList": [
    {
      "mLabel": "_$!<Home>!$_",
      "mNumber": "(988) 034-5678",
      "mType": 1
    }
  ]
}{
  "mAddresses": [
  ],
  "mContactPhoto": "",
  "mDisplayName": ",Sumit,,,,Kumar,,",
  "mPhoneNumberList": [
    {
      "mLabel": "_$!<Home>!$_",
      "mNumber": "(789) 034-5123",
      "mType": 1
    }
  ]
}
Stig Brautaset
  • 2,602
  • 1
  • 22
  • 39
Alexander
  • 25
  • 5
  • 1
    Consider using `NSJSONSerialization JSONObjectWithStream:options:error:`. – rmaddy May 02 '14 at 18:14
  • @rmaddy Yes, I have looked into that. But SBJSON parser let's you parse through custom objects. So wouldn't the native NSJSON deserialiser return null ?!! Regardless, I will give it a try and see if it leads to any success, cheers. – Alexander May 02 '14 at 18:47
  • As I mentioned, the JSONObjectWithStream returns a null for a custom object. My intention is to parse/fetch an object directly from a file input stream instead of fetching a chunk of data and converting it to NSData and then parsing it !!! Is it even possible in the first place ? If not, any other suggestions/alternatives ? – Alexander Dec 04 '14 at 06:52
  • Can you please include an example of the file you're attempting to read? (Not actual names and contact details, of course, but the structure.) – Stig Brautaset Dec 04 '14 at 12:19
  • Please refer to the above question for the structure of the json file. Due to memory constraints we are not inserting one after the other without using an array. Hence, it is not a valid json but it is supported on android surprisingly. – Alexander Dec 04 '14 at 12:53

1 Answers1

0

Your solution looks like it should work to me. If your file so big that you don't want to hold it all in memory, i.e. you want to avoid this line:

NSData *data = [NSData dataWithContentsOfFile:filePath options:NSUTF8StringEncoding error:NULL];

then you can use an NSInputStream (untested, but hopefully you get the gist):

id parser = [SBJson4Parser multiRootParserWithBlock:block
                                       errorHandler:eh];

id is = [NSInputStream inputStreamWithFileAtPath:filePath];
[is open];

// buffer to read from the input stream
uint8_t buf[1024];

// read from input stream until empty, or an error;
// better error handling is left as an exercise for the reader
while (0 > [is read:buffer maxLength: sizeof buffer]) {
    SBJson4ParserStatus status = [parser parse:data];
    NSLog(@"Status: %u",status);
    // handle parser errors here
}
[is close];

However, you still have to read and parse the whole file to guarantee that you find a particular contact. There is no way to read just a specific contact this way. If that is something you do often, you may want to store your contacts a different way that supports that scenario better. One way would be to use e.g. SQLLite.

Stig Brautaset
  • 2,602
  • 1
  • 22
  • 39
  • that worked exactly the way you mentioned but you need to open the stream before reading. I think my question is a bit vague. The real need was to obtain one object from the stream for a function call. The next object is to be parsed only when another function call is made. Bit difficult to control the flow inside the block. – Alexander Dec 10 '14 at 09:35
  • Ah, I see. You want a [pull parser](http://en.wikipedia.org/wiki/XML#Pull_parsing), not a streaming parser. I'm afraid no version of SBJson supports that. – Stig Brautaset Dec 11 '14 at 10:47