Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 442
matths Avatar asked Sep 23 '13 14:09

matths


2 Answers

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.

like image 95
akashivskyy Avatar answered Sep 30 '22 14:09

akashivskyy


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];
}
like image 42
Hlung Avatar answered Sep 30 '22 15:09

Hlung