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
- Use CocoaPods
Or
- Copy files in RPJSONValidator to your project