I'm creating a iphone clients that talks to a wcf service (I do not have control over the server, so no changes can be made here).
I need to send an image to this server, along with some other information. It must be send using json.
I'm using restkit to send and receive data.
The problem is, that the binary data must be send as a byte array. Not as a base64 encoded string.
How do I get from an UIimage to a json string that looks like this
"Picture":{"Content":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,..........
I'm open to a solution that doesn't use restkit.
What is the type in the receiving WCF service? byte[]?
Sounds hokey, but you could try mapping to an NSNumber realtionship and creating an array of them.
Off the top of my head I think that's going to serialize as:
"Picture":{"Content":["1","1","1".....
So you also might need a custom formatter.
Here's a SO post converting a byte array into an NSString. If i'm understanding the issue correctly try converting your byte array into the string then adding that to your feed.
Objective-C - How can I convert Byte Array to NSString?
Here's an excerpt of the accepted answer with code:
NSData *data = [[NSMutableData alloc] init];
uint8_t buffer[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buffer maxLength:1024];
if(len > 0)
{
[data appendBytes:&buffer length:len];
}
NSString *serverText = [[NSString alloc]
initWithData:data
encoding:NSASCIIStringEncoding];
NSLog(#"%#", serverText);
Also worth noting once you get the value into your json, watch out that you don't exceed your json max string length value.
Thanks for the quick replys.
Couldn't get any of the suggested answers to work, so we had to use our mad people skills and make the company who made the server side service change their end so we instead can send a base64 encoded string.
Related
I need a little help with decodeBytesForKey using NSKeyedUnarchiver unarchiveObjectWithFile. I appear to be writing everything correctly but reading the data gives me a EXC_BAD_ACCESS error. I'm trying to deserialize data and have gotten confused by the various answers and examples out there. Can someone please point out what I am doing wrong?
for (NSString *item in directoryContent){
if ([[item pathExtension] isEqualToString:#"template"]) {
**// Next line is the problem line from the calling code:**
templateToRead = [[NSKeyedUnarchiver unarchiveObjectWithFile:[documentsDirectory stringByAppendingPathComponent:item]] mutableCopy];
IDImageTemplate *template = [[IDImageTemplate alloc] init];
template.templateData = templateToRead.templateData;
template.templateQuality = templateToRead.templateQuality;
template.templateSize = templateToRead.templateSize;
template.templateLocation = templateToRead.templateLocation;
[templatesRead addTemplate:template];
}
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeInteger:self.templateSize forKey:#"templateSize"];
[encoder encodeBytes:(const unsigned char*)self.templateData length:self.templateSize forKey:#"templateData"];
[encoder encodeInt:self.templateQuality forKey:#"templateQuality"];
[encoder encodeInt:self.templateLocation forKey:#"templateLocation"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
self.templateSize = [decoder decodeIntegerForKey:#"templateSize"];
**// Next line is where the real problem is:**
self.templateData = (const char *)[decoder decodeBytesForKey:#"templateData" returnedLength:(NSUInteger *)self.templateSize];
self.templateQuality = [decoder decodeIntForKey:#"templateQuality"];
self.templateLocation = [decoder decodeIntForKey:#"templateLocation"];
}
return self;
}
If I comment out the encoder and decoder lines for the data, I get the correct values back for everything else, but I need the data too.
Ok, there are a few different issues here. Thankfully, they're mostly easy to fix if you know C. (As an aside, you should probably spend some time [re-]learning C, since I get the impression that you've so far just gotten by on the basics of Objective-C and haven't learned much about the language it's a superset of.) Anyway, all but one of these has to do with a specific line, so I'll copy it here and chop it down so it's easier to inspect:
self.templateData =
(const char *)[decoder
decodeBytesForKey:#"templateData"
returnedLength:(NSUInteger *)self.templateSize];
Although there's a more glaring issue, the one that's likely causing this particularly crash is this: (NSUInteger *)self.templateSize. I'd guess you were trying to get the address of the templateSize property's instance variable, but this won't work. What you'll actually need to do if you want to pass in the address of that instance variable is something like &_instanceVariable or &this->_instanceVariable (the former usually works since Obj-C allows unqualified instance variable access). (Note: the unary & in the last two code bits is the address-of operator.)
The code you've written is wrong because it's not getting the address of that property's underlying instance variable, it's just casting the returned size to a pointer. So if it returns 0, the pointer is 0; if it returns 4, the pointer is 4; if it returns 42801, the pointer is 42801 (i.e., it's just pointing to whatever those would point to, the first being NULL). So, in the end, decodeBytesForKey:returnedLength: is attempting to write to an address that may or may not be usable memory. It's an easy thing to fix, thankfully.
You could fix #1 and this alone should get it technically working for a short amount of time, but it's still wrong. You also have to take into account that the buffer returned by decodeBytesForKey:returnedLength: is a temporary buffer — it lives only as long as the coder lives. This is mentioned in the discussion segment of decodeBytesForKey:returnedLength:'s docs for NSKeyedUnarchiver and in the NSCoder decodeBytesWithReturnedLength: documentation. Unfortunately, if you're not looking in exactly the right place, that detail might be easy to miss, but it returning a const pointer might be evidence that the result had a temporary lifespan. Anyway, You need to make a copy of the returned buffer if you want to keep that data.
You mentioned in chat that the templateData property is itself a char *, so this means you're going to probably want to allocate a new block of memory of the same size, memcpy the buffer, and store that. This obviously goes with the assumption that you know to also free the buffer.
The easier way to do this, by far, is to just use NSData instead of a pointer to some memory. You can still encode as bytes (which allows you to give it a key) and decode as bytes, but you can get NSData to handle copying the buffer and freeing it for you. E.g.,
NSUInteger size = 0;
const char *buffer = [coder decodeBytesForKey:#"key" returnedLength:&size];
NSData *data = [NSData dataWithBytes:buffer length:size];
And with that, the NSData object and ARC in turn handle that for you. Depending on your specific needs, this might not be preferable, so obviously determine what's important for your specific case.
This is not a specific issue but more of a redundancy thing: you do not have to encode the length of the bytes before encoding the bytes themselves. This is handled by the coder already, hence why it returns its length via the returnedLength: argument (lengthp). So, the lines for encoding and decoding the length are unneeded.
I need to create a JsonString from a NSString, without any key in ObjC.
All tutorials talks about serialization from NSDictionary or NSData.
My string is like #"fr-FR".
The result i'm looking for is like #"{"fr-FR"}", but dynamically.
Can't do it myself because i will need to do the same stuff for different kind of arguments.
Thanks in advance :)
To achieve the result you are asking for (even if it's not a proper JSON) you could try something like this:
NSString *myString = #"fr-FR"; // Or whatever
NSString *result = [NSString stringWithFormat:#"{\"%#\"}", myString];
You need to use a JSON framework/library, for example TouchJSON, and then you can do the following to encode your NSString:
theData = [[CJSONSerializer serializer] serializeObject:theString
error:&theError];
(from the demo code here).
You could use NSJSONSerialization class if you develop on IOS 5 +
create data with your string
NSString *myString = #"fr-FR"; // Or whatever
NSString *result = [NSString stringWithFormat:#"{%#}", myString];
NSData* data=[result dataUsingEncoding:NSUTF8StringEncoding];
and then use
+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error
to create your JSON object
(just typed, not tested)
IMHO after reading the json spec again ( http://www.json.org ) {"string"} isn't valid json and the code you are using in c# produces invalid results. However, if you need to communicate with c# code that acts this way you should just encode the string itself using a JSON library and then afterwards pack it in curly braces. That way you get all the escaping needed for JSON for special cases (e.g. quotes and whatnot) which you don't if you just add quotes and braces manually.
The goal is to have an array where all strings are of length n.
So at the moment what I have my code doing is reading in a plist (which is just 250,000 strings) into an array, and then iterating over the array in order to find which ones are/aren't of length n. Of course, for the sake of efficiency, I'd prefer being able to read in from the plist STRING BY STRING so, as I'm reading in, I may the length then before inserting into the array. I'm just starting to learn objective-c, but I was struggling to Google around for a solution =P
EDIT: Well I just found out I can find much more documentation typing property list rather than plist into google :) so I may be able to figure this out myself
You can parse plist into tree (NSMutableDictionary). Dict will have keys with name of string length.
for example
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSString *str in [plistDict allObjects]) {
NSString *key = [NSString stringWithFormat:#"%d", [str length]];
NSMutableArray *array = [result objectForKey:key];
if (!array) {
array = [NSMutableArray array];
}
[array addObject:str];
[result setObject:array forKey:key];
}
than you can access array with needed strings length
NSArray *string4Lenght = [result objectForKey:#"4"];
Apple doesn't provide an API for incrementally parsing a plist.
If you store your plist in XML format, you could use NSXMLParser to parse it. The schema is pretty simple and somewhat described in the Property List Programming Guide.
If you want to incrementally parse the binary format, you're going to have to do more work. There's no official documentation for the format. Apple's source code for reading and writing the format is open source (CFBinaryPList.c) and there are some useful comments along with the actual code.
If you really need to do it incrementally, I suggest going the XML route. If you do, you might want to subclass NSInputStream to be able to read from a gzip or bzip2 file and decompress on the fly.
initWithData does not convert my data object into a string properly. When I check the length of the data object, it has a value.
NSMutableData* receivedData =[[NSMutableData data] retain];
NSString* json_string = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
Am I doing something wrong creating the string?
As posted, the code is nonsense. You are creating an empty immutable data and then trying to create a string from said empty data.
What does * When I check the length of the data object, it has a value* mean? Do you mean that you have more code that you aren't showing? Something that is filling the mutable data with some bytes?
Also, if the received data is not actually encoded as a UTF-8 string, the conversion will fail. There are a number of methods on NSString that allow for lossy conversion. Try one of those.
I didn't fully complete the NSURLConnection delegate methods. This is where my data is being built.
URL Download
http://code.google.com/p/mwiphonesdk/source/browse/#svn/trunk/iMADE/PrepTasks/08
I have code at the location at the link above and I am using NSMutableString to append strings as data is downloaded. But I am finding that using appendString does not increase the length of the mutable string. I must be doing something wrong.
And when I am done I need to convert NSMutableString to NSString to return it. I have looked for examples to do this but I do not see any yet.
I am most familiar with Java and C# where there is a StringBuffer/StringBuilder which allows you to append pieces and them simply call toString at the end to get a String. It does not appear to be this easy in Objective-C.
NSString* str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
#pragma mark TODO Confirm this is appending a value to the mutable string
[self.mutableString appendString:str];
NSLog(#"str length: %d, %d", [str length], [self.mutableString length]);
Above is the section of code that calls appendString.
I see that str has a length > 0 but calling appendString does not increase the length of self.mutableString.
What am I doing wrong here?
As for having an NSMutableString* and needing to return an NSString*, the former is a subclass of the latter so anywhere you see an NSString* an NSMutableString* will suffice as-is.
Your code looks OK from what you've posted. The only thing I can think of is that perhaps there isn't any data to speak of when initializing the str variable. In such a case appending an empty string will do nothing to mutableString.
You'll also want to make sure self.mutableString has been properly allocated and initialized. You can send messages to NSObject*s that are nil which may be misleading when [self.mutableString length] returns 0.
I have fixed the problem. I simply was not initializing the NSMutableString value and it was transparently not doing anything.
Before appending the string I put the following code.
if (_mutableString == nil){
_mutableString = [[NSMutableString alloc] init];
}
Thanks everyone for answering. And it is good to know that I can use NSMutableString in place of NSString. (that is too easy) :)