8

While using Mantle, is there a possibility to before returning the object we are creating (on this case via JSON) to verify that X and Y properties are not nil?

Imagine this class:

@interface Person : MTLModel <MTLJSONSerializing>

@property(nonatomic,strong,readonly)NSString *name;
@property(nonatomic,strong,readonly)NSString *age;

@end

I want a way to verify that if the JSON I received doesn't have the name (for some reason there was an issue on server's DB) I will return a nil Person, as it doesn't make sense to create that object without that property set.

Rui Peres
  • 25,741
  • 9
  • 87
  • 137

5 Answers5

4

Whilst you could override the initialiser. It seems more concise to override the validate: as this is called at the last stage before Mantle returns the deserialised object. Makes sense to put all your validation logic in a validate method...

See the final line of the MTLJSONAdapter

id model = [self.modelClass modelWithDictionary:dictionaryValue error:error];
return [model validate:error] ? model : nil;

This tells us that if our custom model returns NO from validate, then Mantle will discard the object.

So you could in your subclass simply perform the following:

- (BOOL)validate:(NSError **)error {
    return [super validate:error] && self.name.length > 0; 
}

Ideally in your own implementation you probably want to return an appropriate error.

The validate method will then call Foundation's validateValue:forKey:error: for every property that you registered with Mantle in JSONKeyPathsByPropertyKey. So if you want a more controlled validation setup you could also validate your data here..

Daniel Galasko
  • 23,617
  • 8
  • 77
  • 97
1

You can use the MTLJSONSerializing protocol method classForParsingJSONDictionary: to return nil rather than an invalid object:

// In your MTLModelSubclass.m
//
+ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary {
    if (JSONDictionary[@"name"] == nil || JSONDictionary[@"age"] == nil) {
        return nil;
    }
    return self.class;
}
David Snabel-Caunt
  • 57,804
  • 13
  • 114
  • 132
  • Only downside is that this requires you to hardcode the keys in two places and it doesnt allow you to pass back errors to the system unlike `initWithDictionary:error:` or `validate:` – Daniel Galasko Jun 17 '15 at 15:01
  • Oh, this is not real solution, this is just a hack. Take a look at `validate:` method instead and override it. – Valentin Shergin Oct 30 '15 at 18:37
0

In fact I don't use Mantle, but for validation I use another GitHub Library called RPJSONValidator

It tells you the type that you expect and what type the value has been arrived.

A simple example code

NSError *error;

[RPJSONValidator validateValuesFrom:json
                   withRequirements:@{
                           @"phoneNumber" : [RPValidatorPredicate.isString lengthIsGreaterThanOrEqualTo:@7],
                           @"name" : RPValidatorPredicate.isString,
                           @"age" : RPValidatorPredicate.isNumber.isOptional,
                           @"weight" : RPValidatorPredicate.isString,
                           @"ssn" : RPValidatorPredicate.isNull,
                           @"height" : RPValidatorPredicate.isString,
                           @"children" : RPValidatorPredicate.isArray,
                           @"parents" : [RPValidatorPredicate.isArray lengthIsGreaterThan:@1]
                   } error:&error];

if(error) {
    NSLog(@"%@", [RPJSONValidator prettyStringGivenRPJSONValidatorError:error]);
} else {
    NSLog(@"Woohoo, no errors!");
}

Each key-value pair describes requirements for each JSON value. For example, the key-value pair @"name" : RPValidatorPredicate.isString will place a requirement on the JSON value with key "name" to be an NSString. We can also chain requirements. For example, @"age" : RPValidatorPredicate.isNumber.isOptional will place a requirement on the value of "age" to be an NSNumber, but only if it exists in the JSON.

E-Riddie
  • 14,660
  • 7
  • 52
  • 74
0

I use a very old version of Mantle. YMMV

You can override the [MTLModel modelWithExternalRepresentation] selector. Make sure to call [super modelWithExternalRepresentation] and then add your own code to check for validate data.

John
  • 2,640
  • 1
  • 16
  • 16
0

I followed a small issue opened on Mantle:

- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError *__autoreleasing *)error
{
    BOOL isValid = NO;
    if (self = [super initWithDictionary:dictionaryValue error:error])
    {
        isValid = ...
    }

    return isValid?self:nil;
}

So in the end just override:

- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError *__autoreleasing *)error
Rui Peres
  • 25,741
  • 9
  • 87
  • 137
  • This will prevent you from ever creating a model with a nil age or name (even manually, not just from JSON.) If you only want to prevent this when creating a model from JSON, my answer is more correct. – David Snabel-Caunt Apr 25 '14 at 14:20
  • @DavidCaunt this object only makes sense from JSON, not manually. – Rui Peres Apr 25 '14 at 14:21
  • Then you could prefer my method for not doing all of the Mantle work - it will return nil before mapping the data to property keys, performing transformations etc – David Snabel-Caunt Apr 25 '14 at 14:27
  • @DavidCaunt yes, I will go with your approach as it's more "natural" than mine. – Rui Peres Apr 25 '14 at 14:30