NSMutableString becomes (null) when converted from NSData with length 4000+ - iphone

I'm sending XML request to some server and getting some response data from it using NSURLConnection:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSMutableString *receivedString = [[NSMutableString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
NSLog(#"Size of DATA: %d",[_receivedData length]);
NSLog(#"Body: %#", receivedString);
}
When length of my data is about 4000 - I can normally see the received data in output. But when length of data becomes greater (6000, 10000, etc.), Output shows that my receivedString becomes null!
How can I still save my data to the string, when it's length becomes greater? Thank you.

Are you sure your data is encoded with NSUTF8StringEncoding? Double check the data being returned to you. initWithData:encoding: will return nil if the data is not properly represented by the encoding.
An easy way to test this is to use the same data object, and create a string with incremental pieces of the same data until it fails, then look at the byte that causes it to fail.
I'd bet a piece of southern fried catfish that you have an improperly (or mismatched) encoded byte in that data.

Related

Present JSON string in sorted order from NSDictionary

I made one JSON string from the NSDictionary. The JSON string which I created doesn't come present the items in the order I entered keys and value pair in the NSDictionary .
Now I need all keys and value pairs returned as JSON string in alphabetic order. I tried lot of ways but not getting sorting of JSON string in alphabetic order.
Here's an example of the way the final string is being presented:
{
"SubscriberID" : "603",
"Amount" : "1",
"MerchantID" : "100012",
"Channel" : "Wallet",
"RequestCode" : "0331",
"PosID" : "0465F35F5577CUST",
"TID" : "0000014",
"Stan" : "NA"
}
How do I ensure the items are presented the way I entered them? Or how can I specify the items are alphabetically sorted?
SBJson is able to do this, set sortKeys to YES on SBJsonWriter
http://superloopy.io/json-framework/
SBJsonWriter *writer = [[SBJsonWriter alloc] init];
writer.sortKeys = YES;
// Turn on humanReadable if you also need it pretty-printed
// writer.humanReadable = YES;
NSData *result = [writer dataWithObject:myDictionary];
Like many key-value storage classes, NSDictionary does not guarantee order of the elements you've added. It is a mistake to assume that the key/value pairs, or keyset, will be returned with any particular order.
Further, JSON objects are unordered in the same fashion. You should not care about the order in which the objects are added. Nor should you (or your recipient) rely on being provided a JSON object with 'sorted' keys, as that's not really a valid concept with these particular structures. While ordering the keys might result in an iterator traversing the keys in the order you expect, it's not a guarantee, and should not be relied on.
I think you should revisit why you need the keys sorted in the first place, and see if you can find a way to avoid a dependency on their alphabetical ordering.
Edit: You mention the server requires an SHA hash of the JSON string. If you must, you can create a sorted JSON string by sorting the keys in an NSMutableArray, then creating the JSON string from those keys.
NSMutableArray *sortedKeys = [NSMutableArray arrayWithArray:[myDict allKeys]];
[sortedKeys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSMutableString *jsonString = [[NSMutableString alloc] init];
[jsonString appendString:#"{"];
for (NSString *key in sortedKeys) {
[jsonString appendFormat:#"\"%#\"", key];
[jsonString appendString:#":"];
[jsonString appendFormat:#"\"%#\"", [myDict objectForKey:key]];
[jsonString appendString:#","];
}
if ([jsonString length] > 2) {
[jsonString deleteCharactersInRange:NSMakeRange([jsonString length] - 1, 1)];
}
[jsonString appendString:#"}"];
This code hasn't been tested, so you might need to play with it a bit. Thought it would be much better if you could find a JSON library to do this for you, although you might not have as much control over the key ordering.
You can't do it (or at least you can't rely on that). JSON objects are unordered sets as you can read on JSON.org.
Actually,the system method [NSString stringWithFormat:] is what you need.So here is the easy way:
NSDictionary* inputDictionary = #{
#"SubscriberID" : #"603",
#"Amount" : #"1",
#"MerchantID" : #"100012",
#"Channel" : #"Wallet",
#"RequestCode" : #"0331",
#"PosID" : #"0465F35F5577CUST",
#"TID" : #"0000014",
#"Stan" : #"NA"
};
NSString* sortedJsonStr = [NSString stringWithFormat:#"%#", inputDictionary];
NSLog(#"sorted json string is %#", sortedJsonStr);
The result is:
{
Amount = 1;
Channel = Wallet;
MerchantID = 100012;
PosID = 0465F35F5577CUST;
RequestCode = 0331;
Stan = NA;
SubscriberID = 603;
TID = 0000014;
}
The question does not make sense, and therefore cannot be answered in its current form.
If you encode some JSON as {"keyB":"valueB","keyA":"valueA"}, take it's checksum, and then transmit the encoded JSON and the checksum to the remote site, the remote site has little choice but to take the checksum of the JSON as received and compare that. To do the checksum on sorted values it would have to decode the received JSON string into an NSDictionary, re-encode into "sorted" JSON, and then take the checksum of the reconstituted JSON, and that would be a lot of extra effort for no reason.
Far more likely is that there is some difference in either the way the checksum is being computed (padding of the SHA256 input, eg) or some difference in the JSON strings being used -- code page differences, escaped characters, one end is doing it on Base64 and the other not, etc.

ASIHTTPRequest: incorrect NSStringEncoding value 0x0000 detected

Incorrect NSStringEncoding value 0x0000 detected. Assuming
NSStringEncodingASCII. Will stop this compatiblity mapping behavior in
the near future.
When I was using ASIHTTPRequest, I keep getting this error(50% of the times), what's wrong with it?
I assume the URL I passed in is correct, as it does not contain any space or strange character, maybe it is the result string having some unrecognized character?
Copying my answer from https://stackoverflow.com/q/8251175/918764
The error is not that your data isn't encoded correctly, it's almost certainly that you're requesting some kind of string data from an ASIHTTPRequest object that doesn't have an encoding set yet - this will happen if the request failed to connect to the server, or if the server didn't send an understandable encoding header.
This notably happens if you call [asiHttpRequest getResponseString] before a valid response has been returned and the request encoding has been set (i.e. couldn't connect to server).
The easiest way to workaround this warning is by editing the ASIHTTPRequest class, remove the #synthesize responseEncoding and adding a simple custom getter/setter so you can return the default encoding if the response encoding isn't set:
- (NSStringEncoding) responseEncoding
{
return responseEncoding || self.defaultResponseEncoding;
}
- (void) setResponseEncoding:(NSStringEncoding)_responseEncoding
{
responseEncoding = _responseEncoding;
}
There's also a more specific workaround for the getResponseString method, which I think is the only place that uses the encoding without checking for a value - since the encoding should be set for any non-zero length response:
- (NSString *)responseString
{
NSData *data = [self responseData];
if (!data) {
return nil;
}
// --- INSERT THIS BLOCK ---
// If the 'data' is present but empty, return a simple empty string
if (data.length == 0) {
return #"";
}
//assert(self.responseEncoding); // if you're into runtime asserts, uncomment this
// --- END OF BLOCK TO INSERT ---
return [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:[self responseEncoding]] autorelease];
}

iphone SDK displaying image in uitableview from image data in JSON string

I'm new to this, so here goes..
I'm having a problem with displaying images in uitableview, that are downloaded from a mysql database. Here's what I'm doing:
converting images using UIImagePNGRepresentation.
uploading to MYSQL database via webservice.
So far so good..
The images are downloaded from MYSQL using JSON.
NSDictionary used to create array of image data from JSON String.
[UIImage imageWithData:[imageArray objectAtIndex:indexpath.row]] fails with error: [NSCFString bytes]: unrecongnised selector sent to instance.
I can understand why this is happening, but don't know how to resolve it. The imageWithData is expecting NSData object, but I've converted the string to NSData with no success.
Any help will be greatly appreciated.
Are you converting to and from data properly?
To data:
NSData* theData;
theData = [theNSString dataUsingEncoding:NSASCIIStringEncoding];
To string:
NSString* theNSString;
theNSString = [[NSString alloc] initWithData:theData encoding:NSASCIIStringEncoding];
Thanks for your response.
Yes I was doing the conversion to/from data, as you stated, the only difference being that I was using NSUTF8StringEncoding rather than NSASCIIStringEncoding.
I've tried it with NSASCIIStringEncoding, but the results is the same. It seems that the converted data is different to that stored on the database.
The data from the JSON string (NSData to NSString) is:
<89504e47 0d0a1a0a 0000000d 49484452 00000087 0000005a 08020000 001d25d2 ac000020 00494441 54780174 bd7778dc e775e73b bdf78e19 f40e1004 c002764a ec942cdb b12ccb55 b6e3123b 8e539e44 cecd3ad7 bbc9c6eb f4dc2789 b3297e9c 4d1cc5b1 2dc9b264 4bb22a25 52ec0401 16f45e07 184ceff3 ....
However, the conversion back to NSData gives the following data:
<3c383935 30346534 37203064 30613161 30612030 30303030 30306420 34393438 34343532 20303030 30303038 37203030 30303030 35612030 38303230 30303020 30303164 32356432 20616330 30303032 30203030 34393434 34312035 34373830.....
This may be the same, but [UIImage imageWithData:theData] returns null image.

Compress/Decompress NSString in objective-c (iphone) using GZIP or deflate

I have a web-service running on Windows Azure which returns JSON that I consume in my iPhone app.
Unfortunately, Windows Azure doesn't seem to support the compression of dynamic responses yet (long story) so I decided to get around it by returning an uncompressed JSON package, which contains a compressed (using GZIP) string.
e.g
{"Error":null,"IsCompressed":true,"Success":true,"Value":"vWsAAB+LCAAAAAAAB..etc.."}
... where value is the compressed string of a complex object represented in JSON.
This was really easy to implement on the server, but for the life of me I can't figure out how to decompress a gzipped NSString into an uncompressed NSString, all the examples I can find for zlib etc are dealing with files etc.
Can anyone give me any clues on how to do this? (I'd also be happy for a solution that used deflate as I could change the server-side implementation to use deflate too).
Thanks!!
Steven
Edit 1: Aaah, I see that ASIHTTPRequest is using the following function in it's source code:
//uncompress gzipped data with zlib
+ (NSData *)uncompressZippedData:(NSData*)compressedData;
... and I'm aware that I can convert NSString to NSData, so I'll see if this leads me anywhere!
Edit 2: Unfortunately, the method described in Edit 1 didn't lead me anywhere.
Edit 3: Following the advice below regarding base64 encoding/decoding, I came up with the following code. The encodedGzippedString is as you can guess, a string "Hello, my name is Steven Elliott" which is gzipped and then converted to a base64 string. Unfortunately, the result that prints using NSLog is just blank.
NSString *encodedGzippedString = #"GgAAAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyK+uE6X2SJPiyZ93eaX+TI9Lcuiatvx/wOwYc0HGgAAAA==";
NSData *decodedGzippedData = [NSData dataFromBase64String:encodedGzippedString];
NSData* unGzippedJsonData = [ASIHTTPRequest uncompressZippedData:decodedGzippedData];
NSString* unGzippedJsonString = [[NSString alloc] initWithData:unGzippedJsonData encoding:NSASCIIStringEncoding];
NSLog(#"Result: %#", unGzippedJsonString);
After all this time, I finally found a solution to this problem!
None of the answers above helped me, as promising as they all looked. In the end, I was able to compress the string on the server with gzip using the chilkat framework for .net ... and then decompress it on the iphone using the chilkat framework for iOS (not yet released, but available if you email the guy directly).
The chilkat framework made this super easy to do so big thumbs up to the developer!
Your "compressed" string is not raw GZIP'd data, it's in some encoding that allows those bytes to be stored in a string-- looks like base-64 or something like it. To get an NSData out of this, you'll need to decode it into the NSData.
If it's really base-64, check out this blog post an accompanying code:
http://cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html
which will do what you want.
Once you have an NSData object, the ASIHTTPRequest method will probably do as you like.
This worked for me:
from a string gzipeed, then base64 encoded
to un-gzipped string (all utf8).
#import "base64.h"
#import "NSData+Compression.h"
...
+(NSString *)gunzipBase64StrToStr:(NSString *)stringValue {
//now we decode from Base64
Byte inputData[[stringValue lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];//prepare a Byte[]
[[stringValue dataUsingEncoding:NSUTF8StringEncoding] getBytes:inputData];//get the pointer of the data
size_t inputDataSize = (size_t)[stringValue length];
size_t outputDataSize = EstimateBas64DecodedDataSize(inputDataSize);//calculate the decoded data size
Byte outputData[outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(inputData, inputDataSize, outputData, &outputDataSize);//decode the data
NSData *theData = [[NSData alloc] initWithBytes:outputData length:outputDataSize];//create a NSData object from the decoded data
//NSLog(#"DATA: %# \n",[theData description]);
//And now we gunzip:
theData=[theData gzipInflate];//make bigger==gunzip
return [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
}
#end
I needed to compress data on the iPhone using Objective-c and decompress on PHP. Here is what I used in XCode 11.5 and iOS 12.4:
iOS Objective-c Compression Decompression Test
Include libcompression.tbd in the Build Phases -> Link Binary With Library. Then include the header.
#include "compression.h"
NSLog(#"START META DATA COMPRESSION");
NSString *testString = #"THIS IS A COMPRESSION TESTTHIS IS A COMPRESSION TESTTHIS IS A COMPRESSION TESTTHIS IS A COMPRESSION TESTTHIS IS A COMPRESSION TESTTHIS IS A COMPRESSION TEST";
NSData *theData = [testString dataUsingEncoding:NSUTF8StringEncoding];
size_t src_size = theData.length;
uint8_t *src_buffer = (uint8_t*)[theData bytes];
size_t dst_size = src_size+4096;
uint8_t *dst_buffer = (uint8_t*)malloc(dst_size);
dst_size = compression_encode_buffer(dst_buffer, dst_size, src_buffer, src_size, NULL, COMPRESSION_ZLIB);
NSLog(#"originalsize:%zu compressed:%zu", src_size, dst_size);
NSData *dataData = [NSData dataWithBytes:dst_buffer length:sizeof(dst_buffer)];
NSString *compressedDataBase64String = [dataData base64EncodedStringWithOptions:0];
NSLog(#"Compressed Data %#", compressedDataBase64String);
NSLog(#"START META DATA DECOMPRESSION");
src_size = compression_decode_buffer(src_buffer, src_size, dst_buffer, dst_size, NULL, COMPRESSION_ZLIB);
NSData *decompressed = [[NSData alloc] initWithBytes:src_buffer length:src_size];
NSString *decTestString;
decTestString = [[NSString alloc] initWithData:decompressed encoding:NSASCIIStringEncoding];
NSLog(#"DECOMPRESSED DATA %#", decTestString);
free(dst_buffer);
On the PHP side I used the following function to decompress the data:
function decompressString($compressed_string) {
//NEED RAW GZINFLATE FOR COMPATIBILITY WITH IOS COMPRESSION_ZLIB WITH IETF RFC 1951
$full_string = gzinflate($compressed_string);
return $full_string;
}

NSXMLParser rss issue NSXMLParserInvalidCharacterError

NSXMLParserInvalidCharacterError # 9
This is the error I get when I hit a weird character (like quotes copied and pasted from word to the web form, that end up in the feed). The feed I am using is not giving an encoding, and their is no hope for me to get them to change that. This is all I get in the header:
< ?xml version="1.0"?>
< rss version="2.0">
What can I do about illegal characters when parsing feeds? Do I sweep the data prior to the parse? Is there something I am missing in the API? Has anyone dealt with this issue?
NSString *dataString = [[[NSString alloc] initWithData:webData encoding:NSASCIIStringEncoding] autorelease];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
Fixed my problems...
The NSString -initWithData:encoding: method returns nil if it fails, so you can try one encoding after another until you find one that converts. This doesn't guarantee that you'll convert all the characters correctly, but if your feed source isn't sending you correctly encoded XML, then you'll probably have to live with it.
The basic idea is:
// try the most likely encoding
NSString xmlString = [[NSString alloc] initWithData:xmlData
encoding:NSUTF8StringEncoding];
if (xmlString == nil) {
// try the next likely encoding
xmlString = [[NSString alloc] initWithData:xmlData
encoding:NSWindowsCP1252StringEncoding];
}
if (xmlString == nil) {
// etc...
}
To be generic and robust, you could do the following until successful:
1.) Try the encoding specified in the Content-Type header of the HTTP response (if any)
2.) Check the start of the response data for a byte order mark and if found, try the indicated encoding
3.) Look at the first two bytes; if you find a whitespace character or '<' paired with a nul/zero character, try UTF-16 (similarly, you can check the first four bytes to see if you have UTF-32)
4.) Scan the start of the data looking for the <?xml ... ?> processing instruction and look for encoding='something' inside it; try that encoding.
5.) Try some common encodings. Definitely check Windows Latin-1, Mac Roman, and ISO Latin-1 if your data source is in English.
6.) If none of the above work, you could try removing all bytes greater than 127 (or substitute '?' or another ASCII character) and convert the data using the ASCII encoding.
If you don't have an NSString by this point, you should fail. If you do have an NSString, you should look for the encoding declaration in the <?xml ... ?> processing instruction (if you didn't already in step 4). If it's there, you should convert the NSString back to NSData using that encoding; if it's not there, you should convert back using UTF-8 encoding.
Also, the CFStringConvertIANACharSetNameToEncoding() and CFStringConvertEncodingToNSStringEncoding() functions can help get the NSStringEncoding that goes with the encoding name form the Content-Type header or the <?xml ... ?> processing instruction.
You can also remove that encoding line from xml like this:
int length = str.length >100 ? 100:str.length;
NSString*mystr= [str stringByReplacingOccurrencesOfString:#"encoding=\".*?\""
withString:#""
options:NSRegularExpressionSearch
range:NSMakeRange(0, length)];