NSJSONSerialization parsing error neither dictionary nor array - objective-c

i am succesfully getting data from my server. and after getting it i send the data to the function to parse;
- (void)readIn:(NSMutableData *)s {
NSLog(#"Reading in the following:");
NSString * prints = [[NSString alloc] initWithData:s encoding:NSUTF8StringEncoding];
NSLog(#"%#", prints);
NSError *error = nil;
NSData *jsonData = [[NSData alloc] initWithData:s];
if (jsonData) {
id jsonObjects = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if ([jsonObjects isKindOfClass: [NSArray class]])
NSLog(#"yes we got an Array");
else if ([jsonObjects isKindOfClass: [NSDictionary class]])
NSLog(#"yes we got an dictionary");
else
NSLog(#"neither array nor dictionary!");
if (error) {
NSLog(#"error is %#", [error localizedDescription]);
return;
}
NSArray *keys = [jsonObjects allKeys];
for (NSString *key in keys) {
NSLog(#"%# is %#",key, [jsonObjects objectForKey:key]);
}
} else {
// Handle Error
}
}
now my print on console is:
2012-08-17 13:59:57.667 TaraftarlikOyunu[1157:c07] Reading in the following:
2012-08-17 13:59:57.667 TaraftarlikOyunu[1157:c07] {"uID":"5878341","tm":"fb","hh":122,"pt":75,"coin":500,"ll":1,"qlevel":1,"coect":true,"potWeekly":{"pts":75,"intval":604800000},"acent":{"chamunt":0},"mes":[]}
2012-08-17 13:59:57.668 TaraftarlikOyunu[1157:c07] neither array nor dictionary!
2012-08-17 13:59:57.670 TaraftarlikOyunu[1157:c07] error is The operation couldn’t be completed. (Cocoa error 3840.)
it seems legal json object to me. where am i doing wrong?
i am getting data from the server with nsstream; and here is my code to get data:
case NSStreamEventHasBytesAvailable: {
if(stream == inputStream) {
NSLog(#"inputStream is ready.");
uint8_t buf[1024];
unsigned int len = 0;
len = [inputStream read:buf maxLength:1024];
NSLog(#"length %i", len);
if(len > 0) {
NSMutableData* data=[[NSMutableData alloc] initWithLength:0];
[data appendBytes: (const void *)buf length:len];
[self readIn:data];
}
}
break;
}

Try explicitly setting the jsonObjects to be an array:
NSError *myError = nil;
NSArray *jsonObjects= [NSJSONSerialization JSONObjectWithData:responseData ptions:NSJSONReadingMutableLeaves error:&myError];
for (NSDictionary * dict in jsonObjects) {
NSLog(#"Some data %#", [dict objectForKey:#"field"]);
//replace this to access a valid field
}

The reason for the failure is that the original data probably has some '\' characters in it quoting '"' characters. If you had searched on "Cocoa error 3840" you would have gotten a hint on this. What I suggest you do is print out the original data, one character at a time (its ascii so no need for UTF) and verify this.
char *ptr = [s bytes];
for(int i=0; i<[s length]; ++i) NSLog(#"%c ", *ptr++);

problem is that, json string i get is coming with null termination at the end and when i try to deserialize it it can not be converted to NSDictionary or NSArray. making a little change on the code makes everything perfect. the true code should be like that
case NSStreamEventHasBytesAvailable: {
if(stream == inputStream) {
NSLog(#"inputStream is ready.");
uint8_t buf[1024];
unsigned int len = 0;
len = [inputStream read:buf maxLength:1024];
NSLog(#"length %i", len);
if(len > 0) {
datum =[[NSMutableData alloc] initWithLength:0];
[datum appendBytes: (const void *)buf length:len-1];
NSDictionary * jsondict = [NSJSONSerialization JSONObjectWithData:datum options:NSUTF8StringEncoding error:nil];
NSLog(#"is valid json object %d",[NSJSONSerialization isValidJSONObject:jsondict]);
[self readIn:datum];
}
}
else {
NSLog(#"no buffer!");
}
break;
}
default: {
NSLog(#"Stream is sending an Event: %i", event);
break;
}
}
only difference from the other one is i threw the last byte and it became valid json dictionary.
thanks for the people who are interested in my question.

JSON doesn't accept any control characters other than tab, form feed, carriage return, and line feed in a JSON document, so your code works perfectly fine and does exactly what it is supposed to do by not reading anything.
So where does that nul character come from? Either your code reading the data is wrong, or the server is wrong. Looks to me like the problem is the server. Your "fix" by throwing away the last character is bad - if the server is ever fixed, you'll throw away the closing brace. I'd contact whoever is responsible for the server and fix the problem there.

Related

How to use blocks to handle errors returned by NS methods

I created a file using the following code:
NSMutableString *tabString = [NSMutableString stringWithCapacity:0]; // it will automatically expand
// write column headings <----- TODO
// now write out the data to be exported
for(int i = 0; i < booksArray.count; i++) {
[tabString appendString:[NSString stringWithFormat:#"%#\t,%#\t,%#\t\n",
[[booksArray objectAtIndex:i] author],
[[booksArray objectAtIndex:i] binding],
[[booksArray objectAtIndex:i] bookDescription]]];
}
if (![self checkForDataFile: #"BnN.tab"]) // does the file exist?
[[NSFileManager defaultManager] createFileAtPath:documentsPath contents: nil attributes:nil]; // create it if not
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForWritingAtPath: [NSString stringWithFormat:#"%#/%#",documentsPath, #"BnN.tab"]]; // <---------- userID?
[handle truncateFileAtOffset:[handle seekToEndOfFile]]; // tell handle where's the file fo write
[handle writeData:[tabString dataUsingEncoding:NSUTF8StringEncoding]]; //position handle cursor to the end of file (why??)
This is the code I am using to read back the file (for debugging purposes):
// now read it back
NSString* content = [NSString stringWithContentsOfFile:[NSString stringWithFormat:#"%#/%#",documentsPath, #"BnN.tab"]
encoding:NSUTF8StringEncoding
error: ^{ NSLog(#"error: %#", (NSError **)error);
}];
I am getting 2 build errors on this last statement that says:
Sending 'void (^)(void)' to parameter of incompatible type 'NSError *__autoreleasing *'
and
Use of undeclared identifier 'error'
This is the first time I am using a block to handle method returned errors; I was unable to find any docs in SO or Google showing how to do this. What am I doing wrong?
That function is expecting an NSError** parameter, not a block. The way you should be calling it is something like:
NSError *error = nil;
NSString* content = [NSString stringWithContentsOfFile: [NSString stringWithFormat:#"%#/%#", documentsPath, #"BnN.tab"]
encoding: NSUTF8StringEncoding
error: &error];
if (content == nil) {
NSLog("error: %#", error);
}

Parsing JSON: Checking for object existence and reading values

My application returns a NSMutableData *receivedData.
I've opted for using NSJSONSerialization to parse this under the assumption that it would be easiest. I'm having extreme trouble trying to get my head around how to do it. I'm very new to Objective-C, from a Java background.
In Java I used gson to parse the JSON in to an array which I could use easily. I'm really struggling with this here.
My current code for parsing the JSON is:
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: receivedData options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", e);
} else {
for(NSDictionary *item in jsonArray) {
NSLog(#"Item: %#", item);
}
}
As provided by somebody on the internet. This works and prints two items to NSLog. result and header. Here is how the JSON looks:
{
"header":{
"session":"sessionid",
"serviceVersion":"1",
"prefetchEnabled":true
},
"result":"50ce82401e826"
}
However if there is an error the JSON can also look like this:
{
"header":{
"session":"sessionid",
"serviceVersion":"1",
"prefetchEnabled":true
},
"fault":{
"code":0,
"message":"someErrorCode"
}
}
How I want the code to work:
Check if there is a "fault" object
If there is, print fault.code and fault.message to NSLog
If there isn't, I know that my JSON contains result instead of fault
Print the value of result to NSLog
But I can't for the life of me figure out how to approach it. Can someone please give me some pointers?
your object appears to be a dictionary.
Try this out.
NSError *e = nil;
id jsonObj = [NSJSONSerialization JSONObjectWithData: receivedData options: NSJSONReadingMutableContainers error: &e];
NSArray *jsonArray = nil;
NSDictionary *jsonDict = nil;
if ([jsonObj isKindOfClass:[NSArray class]]){
jsonArray = (NSArray*)jsonObj;
}
else if ([jsonObj isKindOfClass:[NSDictionary class]]){
jsonDict = (NSDictionary*)jsonObj;
}
if (jsonArray != nil) {
// you have an array;
for(NSDictionary *item in jsonArray) {
NSLog(#"Item: %#", item);
}
}
else if (jsonDict != nil){
for (NSString *key in jsonDict.allKeys){
NSLog(#"Key: %# forItem: %#",key,[jsonDict valueForKey:key]);
}
}
else {
NSLog(#"Error: %#",e);
}

NSInputStream read returns unsigned integer maximum value when bytes available

I try to read and write data from/to Socket with NSStream. Here is my code for connect :
- (void)connect
{
[NSStream getStreamsToHostNamed:APIC_HOST_ADDR
port:APIC_HOST_PORT
inputStream:&inStream
outputStream:&outStream];
[inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
inStream.delegate = self;
outStream.delegate = self;
if ([inStream streamStatus] == NSStreamStatusNotOpen)
[inStream open];
if ([outStream streamStatus] == NSStreamStatusNotOpen)
[outStream open];
}
and for input stream i implement the delegate methods to recieve events
- (void)handleInputStreamEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventHasBytesAvailable:
{
int bytesRead;
if (data == nil) {
data = [[NSMutableData alloc] init];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [inStream read:buf maxLength:1024];
if(len>0) {
#try {
[data appendBytes:(const void *)buf length:len];
}
#catch (NSException *exception) {
NSLog(#"Fail: %#", exception);
}
#finally {
NSLog(#"Finally");
bytesRead += len;
}
} else {
NSLog(#"No Buffer");
}
NSString *str = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(#"%#",str);
[str release];
[data release];
data = nil;
} break;
case NSStreamEventErrorOccurred:
{
NSError *theError = [inStream streamError];
NSLog(#"Error reading stream! ,Error %i: %#",[theError code], [theError localizedDescription]);
[self disconnect];
[self connect];
} break;
}
}
[NSStream read:maxLength:] always returns maximum unsigned integer value. Eventually i get this error:
Fail: *** -[NSConcreteMutableData appendBytes:length:]: unable to allocate memory for length (4294967295)
Why does read mehod return this big value? Does it really read that much bytes? (I don't think so) :)
PS: Socket Stream server is ok. it reads and writes data to other clients also and there is no problem.
I resolved the problem. I was writing data without observing if has space available in output stream.
from the NSInputStream read:maxLength documentation:
Return Value
A number indicating the outcome of the operation
A positive number indicates the number of bytes read
0 indicates that the end of the buffer was reached
A negative number means that the operation failed
so in case of end of stream your len is 0, in case of error it is -1 which explains the 4294967295 value on your unsigned int.
so use a signed int and check for negative values.
If from CFReadStreamRead() method returns 1, means the request fails, you should do the processing of failure.
CFReadStreamRead() method to read failure will return 1, with 4294967295-1 is the same block of memory, so the length was 4294967295.

Getting errors in NSError code

Am getting errors in this piece of code; I have placed the error messages in the comments. Can't figure it out.
Thanks in advance.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSMutableString *str = [[NSMutableString alloc]init];
for (int i = 0; i < 10; i++) {
[str appendString:#"Aaron is cool!\n"];
}
// Declare a pointer to an NSError object, but don't instantiate it.
// The NSError instance will only be created if there is, in fact, an error.
NSError *error = nil;
// Pass the error pointer by reference to the NSString method
BOOL success =[str writeToFile:#"/tmp/cool.txt"; // Expected "]"
atomically:YES // Bad receiver type 'int'
encoding:NSUTF8StringEncoding
error:&error];
// Test the returned BOOL, and query the NSError if the write failed
if (success) {
NSLog(#"done writing /tmp/cool.txt");
} else {
NSLog(#"writing /tmp/cool/txt failed:#", error localizedDescription); // Expected ')'
}
}
return 0;
}
If your code doesn't have any typos this is the problem
// Pass the error pointer by reference to the NSString method
BOOL success =[str writeToFile:#"/tmp/cool.txt"; // Expected "]"
atomically:YES // Bad receiver type 'int'
encoding:NSUTF8StringEncoding
error:&error];
Remove the semicolon ";" from here.
BOOL success =[str writeToFile:#"/tmp/cool.txt"; // Expected "]"
try this:
#autoreleasepool {
NSMutableString *str = [[[NSMutableString alloc]init] autorelease];
for (int i = 0; i < 10; i++) {
[str appendString:#"Aaron is cool!\n"];
}
NSError *error = nil;
BOOL success =[str writeToFile:#"/tmp/cool.txt"
atomically:YES
encoding:NSUTF8StringEncoding
error:&error];
if (success) {
NSLog(#"done writing /tmp/cool.txt");
} else {
NSLog(#"writing /tmp/cool/txt failed: %#", [error localizedDescription]);
}
}
return 0;

Can't read file

Here is my method which reads content of file. Unfortunately doesn't work for me. The path to file is correct. What I miss ?
- (IBAction)readFile:(id)sender
{
NSString *str = [NSString stringWithContentsOfFile:#"/Users/joe/text.txt"
encoding:NSUTF32StringEncoding
error:nil
];
NSLog(#"%#", str); //Result is null
}
Try using the built-in functionality of stringWithContentsOfFile:encoding:error: and give it a NSError and you can see exactly whats wrong:
- (IBAction)readFile:(id)sender {
NSError *readFileError = nil;
NSString *str = [NSString stringWithContentsOfFile:#"/Users/joe/text.txt"
encoding:NSUTF32StringEncoding
error:&readFileError
];
if (readFileError) {
NSLog(#"%# - %#", [readFileError localizedDescription], [readFileError localizedFailureReason]);
} else {
NSLog(#"%#", str);
}
}