convert NSObject to NSDictionary [duplicate] - ios

This question already has answers here:
Obj-C easy method to convert from NSObject with properties to NSDictionary?
(6 answers)
Closed 6 years ago.
How do i to convert NSObject to NSDictionary?
At first step i have converted NSDictionary to NSObject like,
QRCodeData *obj = [[QRCodeData alloc] initWithQRcodeData:myDictonary];
QRCodeData.h
#interface QRCodeData : NSObject
-(instancetype)initWithQRcodeData:(NSDictionary*)dictionary;
#end
QRCodeData.m
#implementation QRCodeData
-(instancetype)initWithQRcodeData:(NSDictionary*)dictionary
{
self = [super init];
if(self){
self.name = dictionary[#"userName"];
self.phoneNumber = dictionary[#"mobileNo"];
}
return self;
}
#end
I want my Dictionary from object, it is possible to get?
Please help and thanks in advance..

you can simply get dictionary like,
NSDictionary *dict = #{#"userName": obj.name ,#"mobileNo" : obj.phoneNumber };
here obj is QRCodeData's object.
Hope this will help :)

Easiest way is to add this method in the QRCodeData class.
- (NSDictionary *)dictionaryValue
{
return #{#"userName" : self.name, #"mobileNo" : self.phoneNumber};
}
If userName and phoneNumber could be nil you have to check that.
To call with
NSDictionary *dict = [obj dictionaryValue];

You can use Key-Value Coding(KVC) for this purpose. First, provide class method for all keys you want to share:
+ (NSSet *)keysToCopy
{
return [NSSet setWithObjects:#"userName", #"mobileNio", .....];
}
Then you can do something like in your init method:
for (key in [[self class] keysToCopy])
{
[self setValue:dictionary[key] forKey:key];
}
and provide another method to revert it back to NSDictionary:
- (NSDictionary *)dictionaryRepresentation
{
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (key in [[self class] keysToCopy])
{
[result setObject:[self valueForKey:key] forKey:key];
}
}
The only problem remains that not every property is compatible to NSDictionary storing.
This approach allows you to scale this solution to any Cocoa object and it doesn't require you to change anything but keysToCopy method in case if there are new properties to share.

Related

How can I automatically initialize mutable arrays/ dictionaries for my PFObject subclass? (Parse.com framework)

I have the following code in various parts of my app:
MyPFObjectSubclass *instance = [MyPFObjectSubclass object];
instance.myMutableArray = [NSMutableArray array];
instance.myMutableDictionary = [NSMutableDictionary array];
I am constantly forgetting to initialize these types, and running into problems later when I attempt setValue:forKey or addObject methods.
It's a nice-to-have, I admit, but I would like to play it safe and roll these initializations into +object if possible.
My PFObject subclasses all look roughly like this. I call [MyPFObject tlObject] to create a new instance.
#implementation MyPFObject
#dynamic objectUUID;
// Other fields
+ (MyPFObject*) tlObject
{
MyPFObject* obj = [self object];
[obj tlObjectInit];
// Other initializations
return obj;
}
+ (NSString*) parseClassName
{
return #"MyPFObject";
}
+ (PFQuery*) query
{
PFQuery* query = [PFQuery queryWithClassName: [self parseClassName]];
// Add includeKeys here
return query;
}
#end
I have a category on PFObject that includes tlObjectInit. The field objectUUID is there so that I have a value that can uniquely identify the object BEFORE IT IS SAVED. This is necessary because I sometimes create sets of objects that refer to one another. The Parse objectID is not set until it is saved.
#implementation PFObject (TL)
- (void) tlObjectInit
{
NSString* format = [[self parseClassName] stringByAppendingString: #"-%#"];
[self setObject: [NSUUID uuidStringInFormat: format]
forKey: #"objectUUID"];
}
// Add other initializations here
#end
In your MyPFObjectSubclass, override the init method:
-(instancetype) init
{
if (self = [super init])
{
_myMutableArray = [NSMutableArray array];
_myMutableDictionary = [NSMutableDictionary dictionary];
}
return self;
}
I guess your object method call one way or the other the init method.
Edit:
It looks like you use the Parse framework. As told in the reference, the PFObject init method shouldn't be overridden by subclasses.

Init array from another without modifying its content

I need to make a "clone" of an array to another, but the thing is that when I modify my copied Array, the original is modified too. Using hard copy is not working as I expect.
I'm initializing an array like this:
NSMutableArray *otherArray = [[NSMutableArray alloc] initWithArray: myList copyItems:YES];
where myList is a NSArray that came as a parameter in my function.
The thing is when I need to return myList, it's content has been modified when I modify my otherArray
I tried making a hard copy like:
NSMutableArray* algo = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:myList]];
But, some of the properties are not converted, and a nil value is assigned to them.
Also I tried with this:
NSMutableArray *otherArray = [myList mutableCopy];
Well, the obvious question is: How can I modify a copied object without modifying the original one?
Thanks!
EDIT: Here is my entire function.
RAC(self,filteredPacks) = [RACSignal combineLatest:#[self.searchBoxSignal, self.packListsSignal]
reduce:^NSArray *(NSString *filterString, NSArray *packList) {
NSMutableArray *sweetHelper = [[NSMutableArray alloc] init];
NSMutableArray* packListCopy = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:packList]];
filterString = [filterString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([filterString length] > 0)
{
for(PackList *theList in packListCopy){
NSMutableIndexSet *indexesToDelete = [NSMutableIndexSet indexSet];
NSUInteger currentIndex = 0;
for(Pack *thePack in theList.resolved_packs){
if([thePack.name rangeOfString:filterString options:NSCaseInsensitiveSearch].location == NSNotFound){
[indexesToDelete addIndex:currentIndex];
}
currentIndex++;
}
[theList.resolved_packs removeObjectsAtIndexes:indexesToDelete];
[theList.packs removeObjectsAtIndexes:indexesToDelete];
[sweetHelper addObject:theList];
}
return sweetHelper;
}
else
{
return self.originalList;
}
}
];
Well, I found the solution using this answer and this comment in the same question.
As I said in my question:
"I tried making a hard copy like:
NSMutableArray* algo = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:myList]];
But, some of the properties are not converted, and a nil value is assigned to them."
Well, the properties that were not converted, they weren't because they are custom objects, An I didn't implemented the initWithCode and encodeWithCoder methods. So, when I added those methods to my customObjectClass, my Arrays were hard copied with all their elements.
A little example about what I did:
In my SomeCustomObject.h I should implements NSCoding:
#interface SomeCustomObject : NSObject <NSCoding> {
NSMutableArray * __packs;
int __type;
Link * __selfRef;
NSMutableArray * __resolved_packs;
}
And in my subclass of SomeCustomObject I got something like:
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
__packs = [decoder decodeObjectForKey:#"packs"];
__type = [decoder decodeIntForKey:#"type"];
__selfRef = [decoder decodeObjectForKey:#"selfRef"];
__resolved_packs = [decoder decodeObjectForKey:#"resolved_packs"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:__packs forKey:#"packs"];
[encoder encodeInt:__type forKey:#"type"];
[encoder encodeObject:__selfRef forKey:#"selfRef"];
[encoder encodeObject:__resolved_packs forKey:#"resolved_packs"];
}
I hope this be useful to somebody :)

Assign NSNull Object to NSString

I'm writing an iOS App where i need to get data from a SQL-Database over mobile Services from Azure.
After downloading the data I get a NSDictionary with all attributes from the SQL-Table. If an attribute is empty, the value is NSNull.
Is there a way to pass NSNull to NSString without an IF-Statement (I don't want to have 20 if statements..)?
I wrote a category just for dealing with this issue. I used it with Core Data but it should help you, too.
#interface NSDictionary (Extensions)
- (id)NSNullToNilForKey:(NSString *)key;
#end
#implementation NSDictionary (Extensions)
- (id)NSNullToNilForKey:(NSString *)key
{
id value = [self valueForKey:key];
return value != [NSNull null] ? value : nil;
}
#end
Sample use:
NSString *value = [dictionary NSNullToNilForKey:#"key"];
You can't just assign it, but you can filter out all of the NSNull instances using something like this:
NSDictionary *dictionary = // data from server
NSDictionary *filteredDictionary = [dictionary mutableCopy];
NSSet *keysToRemove = [orig keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
if (obj == [NSNull null]) {
return YES;
} else {
return NO;
}
}];
[filteredDictionary removeObjectsForKeys:[keysToRemove allObjects]];
Now you have the same dictionary except that every key with an NSNull has been removed.

NSdictionary to JSON data is being sorted

I have to send a data by post in JSON format. I have my nsdictionary with keys and values.
NSDictionary *params_country=[NSDictionary dictionaryWithObjectsAndKeys:
#"1111",#"#id",
nil];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"dummy3", #"#name",
#"dummy3#example.com", #"#mail",
#"password",#"#password", params_country,#"country",
nil];
When i am doing a log
DLog(#"params %#",[params description]);
I am getting the following
params {
"#mail" = "dummy3#example.com";
"#name" = dummy3;
"#password" = password;
}
The problem is that i have to sent the JSON in the order that i have listed in the above initialisation of my nsdictionary but the keys are being sorted somehow.
Any solution?
EDIT
Sorry i am sending a nsdictionary also in the params. If i remove the country then its fine.
Dictionaries are an unordered collection type. If you need to maintain a certain order, then you should use an ordered collection type like NSArray. But for this, your web service shouldn't care about the order, since it should be looking up the values by the keys provided.
As per some of the comments, this requirement does not match a valid JSON object as the official JSON Specification states:
An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma).
Unfortunately we don't live in a perfect world with perfect web services and there are often certain things that are out of our control.
I wrote a subclass of NSMutableDictionary after reading up on the internet that will order the dictionary based on the order you call setValue:forKey:.
I put the class into a gist you can download from here: https://gist.github.com/liamnichols/7869468 or you can just copy it from below:
LNOrderedMutableDictionary.h
#interface LNOrderedMutableDictionary : NSMutableDictionary
///If `anObject` is nil, it will not be added to the dictionary.
- (void)setNothingIfNil:(id)anObject forKey:(id)aKey;
#end
LNOrderedMutableDictionary.m
#import "LNOrderedMutableDictionary.h"
#interface LNOrderedMutableDictionary ()
#property (nonatomic, strong) NSMutableDictionary *dictionary;
#property (nonatomic, strong) NSMutableOrderedSet *array;
#end
#implementation LNOrderedMutableDictionary
- (id)initWithCapacity:(NSUInteger)capacity
{
self = [super init];
if (self != nil)
{
self.dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity];
self.array = [[NSMutableOrderedSet alloc] initWithCapacity:capacity];
}
return self;
}
- (id)init
{
self = [self initWithCapacity:0];
if (self)
{
}
return self;
}
- (void)setObject:(id)anObject forKey:(id)aKey
{
[self.array removeObject:aKey];
[self.array addObject:aKey];
[self.dictionary setObject:anObject forKey:aKey];
}
- (void)setNothingIfNil:(id)anObject forKey:(id)aKey
{
if (anObject != nil)
[self setObject:anObject forKey:aKey];
}
- (void)removeObjectForKey:(id)aKey
{
[self.dictionary removeObjectForKey:aKey];
[self.array removeObject:aKey];
}
- (NSUInteger)count
{
return [self.dictionary count];
}
- (id)objectForKey:(id)aKey
{
return [self.dictionary objectForKey:aKey];
}
- (NSEnumerator *)keyEnumerator
{
return [self.array objectEnumerator];
}
#end
If possible, your web service shouldn't have to rely on the JSON objects to be formatted in a specific order but if there is nothing you can do to change this then the above solution is what you are looking for.
Source: cocoawithlove

How to omit null values in JSON Dictionary using Mantle?

I have MyModel inheriting from MTLModel (using the GitHub Mantle pod).
MyModel.h
#import <Mantle/Mantle.h>
#interface MyModel : MTLModel <MTLJSONSerializing>
#property (nonatomic, copy, readonly) NSString *UUID;
#property (nonatomic, copy) NSString *someProp;
#property (nonatomic, copy) NSString *anotherProp;
#end
MyModel.m
#import "MyModel.h"
#implementation MyModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"UUID": #"id",
#"someProp": #"some_prop",
#"anotherProp": #"another"
};
}
}
#end
Now I want to send the JSON to the backend using AFNetworking. Before that I convert the model instance to a JSON NSDictionary to use as parameters/body payload within my request.
NSDictionary *JSON = [MTLJSONAdapter JSONDictionaryFromModel:myModel];
But this JSON consists of strange "" Strings for properties of my model that are nil. What i instead want is Mantle to omit these key/value pairs and just spit out a JSON with only the properties that are not nil or NSNull.null, whatever.
This is a common issue with Mantle and it's called implicit JSON mapping.
MTLJSONAdapter reads all properties of a model to create a JSON string optionally replacing property names with ones given in +JSONKeyPathsByPropertyKey.
If you want some properties to be excluded from the JSON representation of your model, map them to NSNull.null in your +JSONKeyPathsByPropertyKey:
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"UUID": #"id",
#"someProp": #"some_prop",
#"anotherProp": #"another",
#"myInternalProperty": NSNull.null,
#"myAnotherInternalProperty": NSNull.null,
};
}
The implicit JSON mapping has lately become a noticeable problem, a solution for which is currently being discussed at Mantle's home repository at GitHub.
See issues #137, #138, #143 and the current discussion under #149.
EDIT: I clearly misunderstood the question, but now, when I suppose I understand it correctly, the answer is simple.
MTLJSONAdapter generates the JSON data using MTLModel's dictionaryValue property. If you wish to exclude a property from the JSON itself, you can overwrite that method in your MYModel:
- (NSDictionary *)dictionaryValue {
NSMutableDictionary *originalDictionaryValue = [[super dictionaryValue] mutableCopy];
if (self.aPropertyThatShouldBeExcludedWhenNil == nil) {
[originalDictionaryValue removeObjectForKey:#"aPropertyThatShouldBeExcludedWhenNil"];
}
/* repeat the process for other "hidden" properties */
return originalDictionaryValue;
}
EDIT #2: Check out the code* for removing all values that are nil:
- (NSDictionary *)dictionaryValue {
NSMutableDictionary *modifiedDictionaryValue = [[super dictionaryValue] mutableCopy];
for (NSString *originalKey in [super dictionaryValue]) {
if ([self valueForKey:originalKey] == nil) {
[modifiedDictionaryValue removeObjectForKey:originalKey];
}
}
return [modifiedDictionaryValue copy];
}
* - code sample suggested by matths.
I remove nil valued keys by creating an MTLJSONAdapter subclass, and overriding -serializablePropertyKeys:forModel: method.
MTLJSONAdapterWithoutNil.h
/** A MTLJSONAdapter subclass that removes model dictionaryValue keys whose value is `[NSNull null]`. */
#interface MTLJSONAdapterWithoutNil : MTLJSONAdapter
#end
MTLJSONAdapterWithoutNil.m
#import "MTLJSONAdapterWithoutNil.h"
#implementation MTLJSONAdapterWithoutNil
- (NSSet *)serializablePropertyKeys:(NSSet *)propertyKeys forModel:(id<MTLJSONSerializing>)model {
NSMutableSet *ms = propertyKeys.mutableCopy;
NSDictionary *modelDictValue = [model dictionaryValue];
for (NSString *key in ms) {
id val = [modelDictValue valueForKey:key];
if ([[NSNull null] isEqual:val]) { // MTLModel -dictionaryValue nil value is represented by NSNull
[ms removeObject:key];
}
}
return [NSSet setWithSet:ms];
}
#end
And use this to create JSON dictionary instead. Like this:
NSDictionary *JSONDictionary = [MTLJSONAdapterWithoutNil JSONDictionaryFromModel:collection error:nil];
NOTE: if you are overriding NSValueTransformer methods for array or dictionary properties, you also have to change the MTLJSONAdapter class to your subclass as well. Like this:
+ (NSValueTransformer *)myDailyDataArrayJSONTransformer {
return [MTLJSONAdapterWithoutNil arrayTransformerWithModelClass:KBDailyData.class];
}
Overriding - dictionaryValues did not give me the expected behavior
So I created a method for MTL Base class
- (NSDictionary *)nonNullDictionaryWithAdditionalParams:(NSDictionary *)params error:(NSError *)error {
NSDictionary *allParams = [MTLJSONAdapter JSONDictionaryFromModel:self error: &error];
NSMutableDictionary *modifiedDictionaryValue = [allParams mutableCopy];
for (NSString *originalKey in allParams) {
if ([allParams objectForKey:originalKey] == NSNull.null) {
[modifiedDictionaryValue removeObjectForKey:originalKey];
}
}
[modifiedDictionaryValue addEntriesFromDictionary:params];
return [modifiedDictionaryValue copy];
}
The EDIT #2 used to work for me with the previous Mantle code base. Now I have to do the following to continue using EDIT #2:
In file MTLJSONAdapter.m, replace this line:
NSDictionary *dictionaryValue = [model.dictionaryValue dictionaryWithValuesForKeys:propertyKeysToSerialize.allObjects];
with
NSDictionary *dictionaryValue = model.dictionaryValue;
The above is my current workaround to get
{ }
instead of
{
"AddressLine2" : null,
"City" : null,
"ZipCode" : null,
"State" : null,
"AddressLine1" : null
}

Resources