NSJSONSerialization无法parsing有效的JSON – “垃圾在尽头”

我的iOS程序正在接收JSON数据并试图parsing它,但由于某些原因,我无法确定。 multithreading几乎同时调用这个函数。 这一切都开始发生,一旦我切换到使用GCDAsyncSocket,奇怪。 以下是接收和parsing数据的相关代码:

// Called whenever I want my program to receive null-terminated data from the server: [socket readDataToData:[NSData dataWithBytes:"\0" length:1] withTimeout:10 tag:0]; - (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag{ // a GCDAsyncSocket delegate method [self didReceiveNetworkData:data]; } - (void)didReceiveNetworkData: (NSData*) data{ if (TESTING) NSLog(@"Received network data of length %lu===\n%@\n===", (unsigned long) data.length, [[NSString alloc] initWithData: data encoding:NSUTF8StringEncoding]); NSError* error; NSDictionary* json = [NSJSONSerialization JSONObjectWithData: data options:kNilOptions error:&error]; if (!json){ NSLog(@"Got an error parsing received JSON data: %@", error); return; } // Then I handle the dictionary (omitted code)… } 

日志和断点debugging器说这是收到的数据:

 { "responseType": -1 } 

确切地说,它的字节是“{\ n \ t \”responseType \“:\ t-1 \ n} \ 0”。 一旦JSONObjectWithData函数运行,我得到这个“结束时的垃圾”错误:

 Got an error parsing received JSON data: Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn't be completed. (Cocoa error 3840.)" (Garbage at end.) UserInfo=0x10b203800 {NSDebugDescription=Garbage at end.} 

根据在线的JSONtesting人员和我自己的简单testing,在一个单独的项目中,这是有效的JSON。 那么为什么我会得到一个错误? 我认为也许它是在空终止符后抱怨额外的字节,所以我试图修剪它,使NSString的,然后把它转回到NSData,但没有帮助:

 NSDictionary* json = [NSJSONSerialization JSONObjectWithData: [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] dataUsingEncoding: NSUTF8StringEncoding] options:kNilOptions error:&error]; 

因为使用GCDAsyncSocket是什么引发了这个问题,我想知道它是否与multithreading,但我真的想不出任何理由为什么重要。 有任何想法吗?

PS我正在使用GCDAsyncSocket,因为我不知道如何处理Objective-C中的底层套接字,所以我原来的代码有很多奇怪的错误。

更新:你可能有一个与转义字符不知何故的问题。 我自己并不是JSON大师,但是我记得曾经有一个JSONstring被添加到字典中,然后从该字典中创build了一个JSON。 JSONstring本身变成了escapes,所以它在parsing过程中会保留一个string。 我的感觉是你需要删除转义字符。

也许你有一些竞争条件,并正在写入数据,而JSONParsing正在进行。 确保你传递NSData,而不是NSMutableData,以确保序列化的方法。 请记住,NSJSONSerialization不是stream式JSONparsing器。 它需要一个不可变的JSON对象,并将其序列化为一个Foundation对象。 如果您将字节添加到NSData,NSJSONSerialization将在某些点上看到字符stream,在那一点上是不是有效的JSON(还)。

奇怪的是,这是它花了! 在didReceiveNetworkData函数的开始处:

 //Yes, I know this wastefully allocates RAM, so I'll fix it later. NSString* string = [[[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] stringByReplacingOccurrencesOfString:@"\t" withString:@""] stringByReplacingOccurrencesOfString:@"\0" withString:@""]; data = [string dataUsingEncoding:NSUTF8StringEncoding]; 

我删除了所有'\ t'(在条目之前)和'\ 0'(在string的末尾)字符。 删除'\ 0'异或'\ t'不起作用; 它必须是两个。 除非我错过了一些东西,NSJSONSerialization关心制表符和尾随空字符,这似乎很奇怪!

顺便说一句,cJSON编码这个,所以这意味着cJSON和NSJSONSerialization不严格同意。 在线的JSONtesting人员也赞同cJSON编码的方式。

无论如何,我可能是错的,因为这是以前没有GCDAsyncSocket的工作(虽然我仍然不得不删除空字符,我现在回忆)。 或者,也许我的旧解决scheme是以某种方式删除制表符。

即使我要求两次相同的JSON文件,我也遭受了这个问题。 我通过更改我的iOS应用程序如何通过从我自己创build的URL类自定义检索JSON来发出Web请求来解决此问题:

我的老路:

 //init the download JSON class var downloadJSON = createDownloadJSONObject() //Downloads and parses the file correctly downloadJSON.downloadJSON(url: "http://www.example.com/jsonfile.json") /** waits for the first JSON object to return the json file successfully */ //throws the garbage collection error even though requested file is the same downloadJSON.downloadJSON(url: "http://www.example.com/jsonfile.json") 

我只能想象这个问题来自于我如何处理对象的清理。

工作方式:

 //init the download JSON class var downloadJSON = createDownloadJSONObject() //Downloads and parses the file correctly downloadJSON.downloadJSON(url: "http://www.example.com/jsonfile.json") /** waits for the first JSON object to return the json file successfully */ //re instantiate the download JSON class downloadJSON = createDownloadJSONObject() //Downloads and parses the file correctly downloadJSON.downloadJSON(url: "http://www.example.com/jsonfile.json") //happy days 

您可以维护一个字典来存储从服务器获得的响应。 每个任务将有一个唯一的答复。 因此创build一个字典,其中“keys”为任务的“taskIdentifier”,“values”为“data”。

例如:内部didReceiveData或任何其他等效的方法[你从服务器得到响应]存储响应字典与taskIdentifier作为关键。

  NSString *taskID = [@(dataTask.taskIdentifier) stringValue]; [_task_data_dictionary setObject:data forKey:taskID]; 

这里_task_data_dictionary是字典。这样你就可以摆脱上面的错误。

在此之后,您可以使用此代码使用相同的字典获取数据

  NSData *data = [_task_data_dictionary objectForKey:taskNumber]; 

再次使用taskIdentifier。

希望这可以帮助 。