I've tried converting the same NSDictionary object into NSData and then NSString using NSJSONSerialization and SBJsonWriter several times, and sometimes got a different string. even null. It's quite weird and I can't find any reason. =( JSONKit and YAJL don't have problems like this.
Following is my test code.
for (int i = 0; i < 5; i++) {
NSDictionary *d = [NSDictionary dictionaryWithObject:#"value" forKey:#"key"];
NSData *data = [NSJSONSerialization dataWithJSONObject:d options:0 error:nil];
NSLog(#"%#", [NSString stringWithUTF8String:data.bytes]);
}
and the console output is ...
2012-04-25 01:35:33.113 Test[19347:c07] {"key":"value"}
2012-04-25 01:35:33.114 Test[19347:c07] (null)
2012-04-25 01:35:33.114 Test[19347:c07] {"key":"value"}
2012-04-25 01:35:33.114 Test[19347:c07] {"key":"value"}
2012-04-25 01:35:33.115 Test[19347:c07] (null)
output changes every time I run the test code.
data's byte size is the same, but UTF8-converted string length varies.
The bytes in an NSData object do not necessarily comprise a NUL-terminated string. If you want to convert the data into an NSString, do this instead:
[[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding]
There's a possibility that some parsers write '\0' to the end of the data they return for safety, which explains why they behave more predictably. But you shouldn't rely on that behavior, as you've seen.
Related
I have some problems with creating a NSString-representation (JSON-string) of a NSDictionary using NSJSONSerialization. I have used JSONKit before, but since its kind of deprecated in iOS9 (and crashes), I switched to NSJSONSerialization.
This is my code:
// `userSettings` will be of type NSMutableDictionary*
NSData* data= [NSJSONSerialization dataWithJSONObject:userSettings options:0 error:&error];
NSString* settingsString= [NSString stringWithUTF8String:data.bytes];
currentUser.settings= settingsString; // NSString* property
Now, from time to time, this code works, but then sometimes the settingsString will be nil. And when I inspect the data object in the debugger, the bytes property shows the JSON-String followed by some random garbage, like this:
1 = 0x00007ff1ba814000 "{\"residua
...
lculatePlanned\":\"0\",\"wizardUserId\":\"\"}UITextColor\x91UISystemColorName\x94UIBaselineAdjustment\x8cUISystemFont\x89NS.intval\x8eUIShadowOffset\x94UIAutoresizeSubviews\x8dUIContentMode\x85NSRGB\x8aUIFontName\x8bUITextLabel\x8eNSInlinedValue\x91UIDetailTextLabel\x99UIUserInteractionDisabled\x9dUITableCellBackgroundColorSet\x94UINibEncoderEmptyKey\x87NSWhite\x8cNSColorSpace\x8fUITextAlignment\xa3UINibAccessibilityConfigurationsKey\x92UIAutoresizingMask\x99UIProxiedObjectIdentifier\x87UIAlpha\x87UIWhite\x9aUIFontDescriptorAttributes\x8cUIFontTraits\x86NSSize\x95UIColorComponentCount\x91UIMinimumFontSize\x86UIText\x96UIMultipleTouchEnabled\x8dUIDestination\x94UIMi..."
^ start of garbage after end of dictionary
What am I doing wrong?
Do not use + stringWithUTF8String:, it relies on a A NULL-terminated C array of bytes and there is only a NULL terminator by chance and it may be well after the end of the charactery you expect.
Instead use:
- (instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding
Ex:
NSString *settingsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
I know, JSON parsing questions are asked over and over again, but still I can't find any answer to this one.
I've been trying to read and parse a textual JSON file using NSJSONSerialization to no avail.
I've tried using the same JSON data from a NSString and it did work.
Here's the code:
NSError *error;
NSString *jsonString1 = [NSString stringWithContentsOfFile:jsonFilePath
encoding:NSUTF8StringEncoding
error:&error];
NSData *jsonData1 = [jsonString1 dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonObject1 = [NSJSONSerialization JSONObjectWithData:jsonData1
options:0
error:&error];
NSString *jsonString2 = #"{\"key\":\"value\"}";
NSData *jsonData2 = [jsonString2 dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonObject2 = [NSJSONSerialization JSONObjectWithData:jsonData2
options:0
error:&error];
- The text file contains one line: {"key":"value"}
- jsonString1 = #"{"key":"value"}"
- jsonString2 = #"{\"key\":\"value\"}"
- jsonData1 is 23 bytes in size
- jsonData2 is 15 bytes in size
- jsonObject1 is nil and I get error code 3840
- jsonObject2 is a valid dictionary
Seems like the problem is with reading the file, since the NSStrings and NSDatas differ, but what am I doing wrong here and how can I fix it?
Most likely you file contains some unprintable characters (e.g. \0) that trigger the failure. Printing the error message will tell you at what position the first invalid characters occurs.
For example, try printing "{\"key\":\u{0000}\"value\"}" and you'll seem to get a valid JSON, however decoding it fails.
I always do a check on the return value when doing anything with NSUTF8StringEncoding and if nil, then try NSASCIIStringEncoding:
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
if (jsonString == nil) {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSASCIIStringEncoding];
}
return jsonString;
I have a REST API which expects a file size field to be in bytes. However I am observing that NSJSONSerialization is converting my value to an exponential representation which my server doesn't support.
For example:
unsigned long long fileSize = 100000000000;
NSDictionary *myObject = #{"fileSize": #(fileSize)};
NSData *dataToSend = [NSJSONSerialization dataWithJSONObject:myObject options:0 error:nil];
// send dataToSend to network
On the wire I observe the following is sent:
{ "fileSize" : 1.0e+11 }
Is there any way to force NSJSONSerialization to retain the unsigned long long formatting on the wire?
E.g.
{ "fileSize" : 100000000000 }
Update: Corrected my sample code thanks to Gabriele Petronella
Turns out I was able to solve this just now by explicitly initializing the NSNumber as an unsigned long long.
E.g.
unsigned long long fileSize = 100000000000;
NSDictionary *myObject = #{"fileSize": [NSNumber numberWithUnsignedLongLong:fileSize]};
NSData *dataToSend = [NSJSONSerialization dataWithJSONObject:myObject options:0 error:nil];
// send dataToSend to network
This resulted in the output I expected:
{ "fileSize" : 100000000000 }
I don't see any NSJSONSerialization in your question, anyway converting the dictionary to a NSData instance seems wrong, and it's likely to be causing this encoding issue. Just do something like
NSDictionary *myObject = #{"fileSize": #(fileSize)};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:myObject options:0 error:nil];
NSData* jsonData is the http response contains JSON data.
NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"jsonString: %#", jsonString);
I got the result:
{ "result": "\u8aaa" }
What is the proper way to encoding the data to the correct string, not unicode string like "\uxxxx"?
If you convert the JSON data
{ "result" : "\u8aaa" }
to a NSDictionary (e.g. using NSJSONSerialization) and print the dictionary
NSError *error;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSLog(#"%#", jsonDict);
then you will get the output
{
result = "\U8aaa";
}
The reason is that the description method of NSDictionary uses "\Unnnn" escape sequences
for all non-ASCII characters. But that is only for display in the console, the dictionary is correct!
If you print the value of the key
NSLog(#"%#", [jsonDict objectForKey:#"result"]);
then you will get the expected output
說
I don't quite understand what the problem is. AFNetworking has given you a valid JSON packet. If you want the above code to output the character instead of the \u… escape sequence, you should coax the server feeding you the result to change its output. But this shouldn't be necessary. What you most likely want to do next is run it through a JSON deserializer…
NSDictionary * data = [NSJSONSerialization JSONObjectWithData:jsonData …];
…and you should get the following dictionary back: #{#"result":#"說"}. Note that the result key holds a string with a single character, which I'm guessing is what you want.
BTW: In future, I suggest you copy-paste output into your question rather than transcribing it by hand. It'll avoid several needless rounds of corrections and confusion.
I'm developing an iOS application , that will take a twits from twitter,
I'm using the following API
https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&count=2&screen_name=TareqAlSuwaidan
The problem are feed in Arabic Language ,
i.e the text feed appears like this
\u0623\u0646\u0643 \u0648\u0627\u0647\u0645
How can i get the real text (or how to encode this to get real text) ?
This is not encrypted, it is unicode. The codes 0600 - 06ff is Arabic. NSString handles unicode.
Here is an example:
NSString *string = #"\u0623\u0646\u0643 \u0648\u0627\u0647\u0645";
NSLog(#"string: '%#'", string);
NSLog output:
string: 'أنك واهم'
The only question is exactly what problem are you seeing, are you getting the Arabic text? Are you using NSJSONSerialization to deserialize the JSON? If so there should be no problem.
Here is an example with the question URL (don't use synchronous requests in production code):
NSURL *url = [NSURL URLWithString:#"https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&count=2&screen_name=TareqAlSuwaidan"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSArray *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSDictionary *object1 = [jsonObject objectAtIndex:0];
NSString *text = [object1 objectForKey:#"text"];
NSLog(#"text: '%#'", text);
NSLog output:
text: '#Naser_Albdya أيدت الثورة السورية منذ بدايتها وارجع لليوتوب واكتب( سوريا السويدان )
Those are Unicode literals. I think all that's needed is to use NSString's stringWithUTF8String: method on the string you have. That should use NSString's native Unicode handling to convert the literals to the actual characters. Example:
NSString *directFromTwitter = [twitterInterface getTweet];
// directFromTwitter contains "\u0623\u0646\u0643 \u0648\u0627\u0647\u0645"
NSString *encodedString = [NSString stringWithUTF8String:[directFromTwitter UTF8String]];
// encodedString contains "أنك واهم", or something like it
The method call inside the conversion call ([directFromTwitter UTF8String]) is to get access to the raw bytes of the string, that are used by stringWithUTF8String. I'm not exactly sure on what those code points come out to, I just relied on Python to do the conversion.