Как опустить нулевые значения в словаре JSON с помощью мантии?

У меня есть MyModel, наследующий от MTLModel (с помощью 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

Теперь я хочу отправить JSON на бэкэнд, используя AFNetworking. До этого я конвертирую экземпляр модели в JSON NSDictionary для использования в качестве параметров/полезной нагрузки тела в моем запросе.

NSDictionary *JSON = [MTLJSONAdapter JSONDictionaryFromModel:myModel];

Но этот JSON состоит из странных "строк" ​​для свойств моей модели, которые равны нулю. То, что я вместо этого хочу, это Mantle, чтобы опустить эти пары ключ/значение и просто выплюнуть JSON только с свойствами, которые не ноль или NSNull.null, что бы то ни было.

Ответы

Ответ 1

Это обычная проблема с Mantle и она называется неявным отображением JSON.

MTLJSONAdapter считывает все свойства модели для создания строки JSON, необязательно заменяя имена свойств на те, которые указаны в +JSONKeyPathsByPropertyKey.

Если вы хотите, чтобы некоторые свойства были исключены из JSON-представления вашей модели, сопоставьте их с NSNull.null в +JSONKeyPathsByPropertyKey:

+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{
        @"UUID": @"id",
        @"someProp": @"some_prop",
        @"anotherProp": @"another",
        @"myInternalProperty": NSNull.null,
        @"myAnotherInternalProperty": NSNull.null,
    };
}

Неявное сопоставление JSON в последнее время стало заметной проблемой, решение для которой в настоящее время обсуждается в репозитории Mantle home в GitHub.

См. вопросы # 137, # 138, # 143 и текущее обсуждение под # 149.


РЕДАКТИРОВАТЬ: Я четко не понял этот вопрос, но теперь, когда я полагаю, что я правильно его понимаю, ответ прост.

MTLJSONAdapter генерирует данные JSON с использованием свойства MTLModel dictionaryValue. Если вы хотите исключить свойство из самого JSON, вы можете перезаписать этот метод в 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: Проверьте код * для удаления всех значений 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.суб >

Ответ 2

Я удаляю nil ценные ключи, создавая подкласс MTLJSONAdapter и переопределяя метод -serializablePropertyKeys:forModel:.

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

И используйте это, чтобы вместо этого создать словарь JSON. Вот так:

NSDictionary *JSONDictionary = [MTLJSONAdapterWithoutNil JSONDictionaryFromModel:collection error:nil];

ПРИМЕЧАНИЕ: , если вы переопределяете методы NSValueTransformer для свойств массива или словаря, вам также необходимо изменить класс MTLJSONAdapter на ваш подкласс. это:

+ (NSValueTransformer *)myDailyDataArrayJSONTransformer {
    return [MTLJSONAdapterWithoutNil arrayTransformerWithModelClass:KBDailyData.class];
}

Ответ 3

Overriding - dictionaryValues ​​не дал ожидаемого поведения

Итак, я создал метод для базового класса MTL

    - (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];
    }

Ответ 4

EDIT # 2 использовался для меня с предыдущей базой кода мантии. Теперь я должен сделать следующее, чтобы продолжить использовать EDIT # 2:

В файле MTLJSONAdapter.m замените эту строку:

NSDictionary *dictionaryValue = [model.dictionaryValue dictionaryWithValuesForKeys:propertyKeysToSerialize.allObjects];

с

NSDictionary *dictionaryValue = model.dictionaryValue;

Вышеупомянутое мое текущее обходное решение, чтобы получить

{ }

вместо

{
  "AddressLine2" : null,
  "City" : null,
  "ZipCode" : null,
  "State" : null,
  "AddressLine1" : null
}