• Stars
    star
    469
  • Rank 93,595 (Top 2 %)
  • Language
    Objective-C
  • License
    MIT License
  • Created over 10 years ago
  • Updated over 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Validate JSON before it is mapped

RPJSONValidator

Validate JSON before it is mapped

Given

NSDictionary *json = @{
            @"phoneNumber" : @"123-555-6789",
            @"name" : @"Johnny Ringo",
            @"age" : @"BANANA",
            @"weight" : @"130.3",
            @"ssn" : [NSNull null],
            @"children" : @[],
            @"parents" : @[
                    @{
                            @"name" : @"Mickey"
                    },
                    @{
                            @"name" : @"Minnie"
                    }
            ]
    };

Before

BOOL validated = YES;

NSString *phoneNumber = [json objectForKey:@"phoneNumber"];
if(!phoneNumber || ![phoneNumber isKindOfClass:[NSString class] || [phoneNumber length] < 7) {
  NSLog(@"Phone number didn't validate (not found or not an NSString or length < 7)");
  validated = NO;
}

NSString *name = [json objectForKey:@"name"];
if(!name || ![phoneNumber isKindOfClass:[NSString class]) {
  NSLog(@"Phone number didn't validate (not found or not an NSString)");
  validated = NO;
}

NSNumber *age = [json objectForKey:@"age"];
if(age && ![age isKindOfClass:[NSNumber class]) {
  NSLog(@"Age exists but didn't validate (not an NSNumber)");
  validated = NO;
}

NSNumber *weight = [json objectForKey:@"weight"];
if(weight && ![weight isKindOfClass:[NSString class]) {
  NSLog(@"Weight exists but didn't validate (not an NSString)");
  validated = NO;
}

NSString *ssn = [json objectForKey:@"ssn"];
if(ssn != [NSNull null]) {
  NSLog(@"ssn should be null");
  validated = NO;
}

NSString *height = [json objectForKey:@"height"];
if(height && ![weight isKindOfClass:[NSString class]) {
  NSLog(@"Height exists but didn't validate (not an NSString)");
  validated = NO;
}

NSArray *children = [json objectForKey:@"children"];
if(children && ![children isKindOfClass:[NSArray class]) {
  NSLog(@"Children exists but didn't validate (not an NSArray)");
  validated = NO;
}

NSArray *parents = [json objectForKey:@"parents"];
if(parents && ![parents isKindOfClass:[NSArray class] && [parents count] <= 1) {
  NSLog(@"Parents exists but didn't validate (not an NSArray or count <= 1)");
  validated = NO;
}

After

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!");
}

Explanation

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.

Predicates

  • isOptional
    • Evaluate the other predicates only if the key exists
  • hasSubstring:(NSString *)substring
    • Require NSString with substring
  • isString
  • isNumber
  • isDictionary
  • isArray
  • isBoolean
  • isNull
    • Require value == [NSNull null]
  • isNotNull
    • Require value != [NSNull null]
  • validateValueWithBlock:(BOOL (^)(NSString *jsonKey, id jsonValue))block
    • For custom validations, given the JSON key and corresponding value (could be nil, [NSNull null], NSNumber, NSArray, NSDictionary, or NSString), return YES if valid and NO if invalid
  • lengthIsLessThan:(NSNumber *)value
  • lengthIsLessThanOrEqualTo:(NSNumber *)value
  • lengthIsEqualTo:(NSNumber *)value
  • lengthIsNotEqualTo:(NSNumber *)value
  • lengthIsGreaterThanOrEqualTo:(NSNumber *)value
  • lengthIsGreaterThan:(NSNumber *)value
  • valueIsLessThan:(NSNumber *)value
  • valueIsLessThanOrEqualTo:(NSNumber *)value
  • valueIsEqualTo:(NSNumber *)value
  • valueIsNotEqualTo:(NSNumber *)value
  • valueIsGreaterThanOrEqualTo:(NSNumber *)value
  • valueIsGreaterThan:(NSNumber *)value
  • matchesRegularExpression:(NSRegularExpression *)expression
  • valuesWithRequirements:(NSDictionary *)requirements
    • Evaluate an array with requirements

But Wait, There's More!

Pretty Printing

NSDictionary *json = @{...};
NSError *error;

[RPJSONValidator validateValuesFrom:json
                   withRequirements:@{...}
                              error:&error];

NSLog(@"%@", [RPJSONValidator prettyStringGivenRPJSONValidatorError:error];

// Output:
// 2014-03-19 23:08:02.451 RPJSONValidator[42273:60b] 
// * age
//      * Requires NSNumber, given (__NSCFConstantString)
// * height
//      * Key not found
// * parents
//      * Requires NSString, given (__NSArrayI)
//      * Requires length or count less than or equal to (3)

Sub-JSON Validating

NSDictionary *json = @{
        @"car" : @{
                @"make" : @"Ford",
                @"model" : @"Mustang"
        },
};

[RPJSONValidator validateValuesFrom:json
                           withRequirements:@{
                                   @"car" : @{
                                           @"make" : [RPValidatorPredicate valueIsEqualTo:@"Ford"],
                                           @"model" : [RPValidatorPredicate valueIsEqualTo:@"Mustang"]
                                   }
                                      error:&error]

Validate by Index

NSDictionary *json = @{
        @"cars" : @[
            @{
                 @"make" : @"Ford",
                 @"model" : @"Mustang"
            },
            @{
                 @"make" : "Tesla Motors",
                 @"model" : "Model S"
            },
            ...
        ],
};

[RPJSONValidator validateValuesFrom:json
                           withRequirements:@{
                                   @"cars" : @{
                                        @0 : @{ // Access the first element
                                             @"make" : RPValidatorPredicate.isString,
                                             @"model" : RPValidatorPredicate.isString
                                        }
                                   }
                                      error:&error]

Validate an array

NSDictionary *json = @{
        @"friends" :  @[
                @{@"name" : @"Anna", @"age" : @25},
                @{@"name" : @"Maria", @"age" : @19},
                @{@"name" : @"WrongObject", @"counry" : @"UA"}]
};

NSError *error;
[RPJSONValidator validateValuesFrom:json
                       withRequirements:@{
                               @"friends" : [RPValidatorPredicate.isArray valuesWithRequirements:@{
                                       @"name" : RPValidatorPredicate.isString,
                                       @"age"  : RPValidatorPredicate.isNumber}]
                       }
                                  error:&error]

if(error) {
    // Something in the array isn't valid
} else {
    // Every element in the array is valid
}

Requirements

Install

Or

  • Copy files in RPJSONValidator to your project