NSDictionary to NSDictionary formatted string and back - objective-c

I am trying to convert two formats of NSDictionary formatted strings to and from each other.
Both strings:
<key>info</key><dict><key>title</key><string>The Title</string></dict>
and
info = { title = "The Title";}
convert to an NSDictionary if I use initWithData or initWithContentsofFile. When I use [NSDictionary description] to get a string from a dictionary, I get the second string.
So it is a one way conversion if I input the first string. Is there an easy way to convert the second string back to the first and also use them together in a file like below and convert that to NSDictionary? Thanks.
<dict>
dict1 contents...
</dict>
info ={dict2 contents...}

Solved my problem, many thanks to the comments.
To save in "human readable plist form"/"property list representation" (XML, etc) either use
NSDictionary's writeToFile: method. Doc link
or
NSPropertyListSerialization dataWithPropertyList: method. Doc link
NSError *error;
dataToBeStored = [NSPropertyListSerialization dataWithPropertyList:dictionaryToBeStored format:NSPropertyListXMLFormat_v1_0 options:0 error:&error];
if (data) {
NSFileManager* fileManager = [[NSFileManager alloc] init];
[fileManager createFileAtPath:fullPath contents:dataToBeStored attributes:nil];
[fileManager release];
}
Now if you open the saved file up and read the data and convert to string you get a nice human readable plist. Or you can also init an NSDictionary with it.

Related

Can't write to custom .plist

Please consider the following code:
- (IBAction)testButton:(id)sender{
//create BFF with random id
BFF * testBff = [[BFF alloc]init];
testBff.relationType=#"BFF";
testBff.id= [NSNumber numberWithInt:(arc4random() % 100)];
testBff.handshake=[NSDate date];
//Encode the object
//Since a .plist doesn't take custom objects, I convert my object into type NSData*
NSData *encodedBFF = [NSKeyedArchiver archivedDataWithRootObject:testBff];
//Insert the data into the plist and save
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"myBFFS.plist"];
[data setObject: encodedBFF forKey:testBff.id];
[data writeToFile:path atomically:YES];
}
I would expect that every time I hit the button connected to this method, a new object would be added to myBFFs.plist residing in my documents folder.
-The Path seems to be fine
-The Variables Inspector indicates that the 'data' NSDictionary contains a key with a value of type NSMutableData
Somehow, however, nothing is written to a file.
If I change
[data setObject: encodedBFF forKey:testBff.id];
to
[data setObject: #"foo" forKey:#"bar"];
a file IS written. So apparently, my encoding attempts failed?
What am I doing wrong here?
I solved it as follows:
Apparently, the culprit was setting the key in this line:
[data setObject: encodedBFF forKey:testBff.id];
which I changed to
[myPlist setObject:encodedBFF forKey:[testBff.id stringValue]];

Read .mobileprovisioning profile with Objective-C

So, I'm trying to open a .mobileprovisioning profile to read what's inside... this is what I'm doing:
NSString *path = [pathURL path];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
Of course I get the data read but I'm not finding the way of getting of get this data into something useful... an NSDictionary, an NSString or whatever...
I've already tried:
NSString *newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
Any idea? I'm sure this is an encoding issue, but I can't solve it after reading and googling for some time... I think the provisioning profile is saved as hexadecimal, but I don't know how to read that from objective-c. I have found this but there wasn't an useful answer.
How to convert NData populated with hex values to NSString
Thanks!
The following method should do what you want. As #rbrockerhoff says the mobile provisioning profile is an encoded CMS message. This method uses a decoder to first decode the data using the CMS functions and then creates the plist string/contents from the decoded data. This string can then be converted into a dictionary which is returned from the method. The dictionary will contain all the details from the mobile provisioning profile.
- (NSDictionary *)provisioningProfileAtPath:(NSString *)path {
CMSDecoderRef decoder = NULL;
CFDataRef dataRef = NULL;
NSString *plistString = nil;
NSDictionary *plist = nil;
#try {
CMSDecoderCreate(&decoder);
NSData *fileData = [NSData dataWithContentsOfFile:path];
CMSDecoderUpdateMessage(decoder, fileData.bytes, fileData.length);
CMSDecoderFinalizeMessage(decoder);
CMSDecoderCopyContent(decoder, &dataRef);
plistString = [[NSString alloc] initWithData:(__bridge NSData *)dataRef encoding:NSUTF8StringEncoding];
NSData *plistData = [plistString dataUsingEncoding:NSUTF8StringEncoding];
plist = [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListImmutable format:nil error:nil]
}
#catch (NSException *exception) {
NSLog(#"Could not decode file.\n");
}
#finally {
if (decoder) CFRelease(decoder);
if (dataRef) CFRelease(dataRef);
}
return plist;
}
A .mobileprovisioning file is an encoded CMS message.
See https://developer.apple.com/library/mac/documentation/security/Reference/CryptoMessageRef/Reference/reference.html for details and an API for decoding it.
If you just want the encoded property list as text, a quick-and-dirty hack is to get the byte pointer for your NSData, scan for the beginning "<?xml" and up to the closing "</plist>". Then make a NSString from that.
You can simply force to open the mobile provisioning profile in TextEdit where you can see the
interior contents and in which you can trim/Edit the encoded CMS message or whatever you want . Then you can simply decode with NSData encodewithUTF string method.
Hope this helps.

How to convert json string to nsdictionary on json parser framework on objective c

I am trying to convert raw json string to NSDictionary. but on NSDictionary i got different order of objects as on json string but i need exactly same order in NSDictionary as on json string. following is code i have used to convert json string
SBJSON *objJson = [[SBJSON alloc] init];
NSError *error = nil;
NSDictionary *dictResults = [objJson objectWithString:jsonString error:&error];
From NSDictionary's class reference:
The order of the keys is not defined.
So, basically you can't do this when using a standard NSDictionary.
However, this may be a good reason for subclassing NSDictionary itself. See this question about the details.
NSDictionary is an associative array and does not preserve order of it's elements. If you know all your keys, then you can create some array, that holds all keys in correct order (you can also pass it with your JSON as an additional parameter). Example:
NSArray* ordered_keys = [NSArray arrayWithObjects: #"key1", #"key2", #"key3", .., nil];
for(NSString* key is ordered_keys) {
NSLog(#"%#", [json_dict valueForKey: key]);
}
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray* latestLoans = [json objectForKey:#"loans"]; //2
NSLog(#"loans: %#", latestLoans); //3
Source: Follow this link http://www.raywenderlich.com/5492/working-with-json-in-ios-5
Good tutorial but works only on iOS5

How to create JSONP on MacOS?

I use the following code to create a JSON file.
// Some data in keys and vals.
NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:vals forKeys:keys];
NSError* writeError = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:NSJSONWritingPrettyPrinted error:&writeError];
NSString* path = #"json.txt";
[jsonData writeToFile:path atomically:YES];
How can I output a JSONP file? Is there a Cocoa framework I can use?
Update: In the meantime, I used a quick-and-dirty solution: I read in the JSON file just written before to the disc and add the missing JSONP-function to the string. Then, I write the file a second time. I think that's not worth being the answer to my question. So I will leave this question open to a smarter solution.
You could convert the JSON data to a string, wrap it in your function call and then write it to a file. Example:
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:NSJSONWritingPrettyPrinted
error:NULL];
NSMutableString *jsonString = [[[NSMutableString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding] autorelease];
[jsonString insertString:#"functionCall(" atIndex:0];
[jsonString appendString:#");"];
[jsonString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
(I'm using a mutable string here for better memory efficiency.)
I don't know objective-c or Cocoa. (I use python on MacOS to create JSNOP responses), but it's a simple thing to do.
The basic idea is to wrap the JSON data in a javascript function call:
functionCall({"Name": "Foo", "Id" : 1234, "Rank": 7});
The tricky part is that the function name, "functionCall", is set by the browser and AFAIK the name of that query parameter is not standardized. jQuery uses jsonCallback. Other's use json or callback. So the request url must be checked for that callback name and that function name must be used to wrap the json data.

NSData writeToFile writes Plist successfully, but then crashes, giving NSInvalidArgumentException

I am converting a JSON file to a plist using the new NSJSONSerialization class and NSPropertyListSerialization class. I manage to convert my JSON to a Plist without errors, but then, at my last step, when I go to write the plist to my desktop, the program crashes, but AFTER the Plist has been generated!
NSData *data = [[NSData alloc] initWithContentsOfURL:path]; \\(NSURL *)path -->goes to my JSON file
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:nil];
//the following removes all key/object pairs where the object is null, because NSPropertyListSerialization with throw an error if there are null values
for (id __strong object in [json objectForKey:#"terms"]) {
if ([object objectForKey:#"image"] == [NSNull null]) {
[object removeObjectForKey:#"image"];
}
}
//the following NSPropertyListSerialization method returns an NSData
id plist = [NSPropertyListSerialization dataFromPropertyList:(id)json
format:NSPropertyListXMLFormat_v1_0
errorDescription:nil];
NSError *writeToFileError;
[plist writeToFile:#"/Users/kalaracey/Desktop/test.plist"
atomically:YES
encoding:NSUTF8StringEncoding
error:&writeToFileError];
Then, at this last line, an NSInvalidArgumentException is thrown, and crashes my program. However, the plist was successfully generated! I can read it, and all is well, except my program crashes.
Could someone please explain why this crashes, and how I could avoid crashing?
The problem seems to be that the variable plist is type id. Cast it to NSData and you should be fine.
NSData *plist = (NSData *) [NSPropertyListSerialization ...];
As you correctly point out in the comment, NSData should use the writeToFile:atomically: method.