Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fix crash on invalid key path #230

Merged
merged 5 commits into from

2 participants

@dcordero

Fixed a crash when an invalid key path is indicated for a key in the JSONKeyPathsByPropertyKey method

David Cordero added some commits
David Cordero Add podspec file 22712f9
David Cordero Merge remote-tracking branch 'upstream/master' 1a6be4e
David Cordero Fix a crash when an invalid keypath is used 2807e4e
David Cordero Revert "Add podspec file"
This reverts commit 22712f9863a01091ad9f6b0bb186382204f8583e.
837bdd5
@jspahrsummers

It's a little hard to tell what this is fixing. From the test, am I correct in understanding that it fixes a crash when a nested key path is specified in +JSONKeyPathsByPropertyKey, but the JSON contains a primitive instead of an object at some part of that path?

If so, we should fix this by stepping through the JSON dictionary manually, not catching exceptions. Exceptions in Objective-C are generally used to indicate unrecoverable errors, so catching them is rarely safe. (Mantle does so as a convenience for release code, and the value of even that has been debated.)

@dcordero

Yep it fix exactly that case. What exactly you mean with "stepping through the JSON dictionary manually" ? I mean, given the test case:

NSDictionary *values = @{
    @"username": @"foo",
    @"nested": @"bar",
    @"count": @"0"
    };

with a + JSONKeyPathsByPropertyKey like this:

+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{
        @"name": @"username",
        @"nestedName": @"nested.name",
        @"weakModel": NSNull.null,
    };
}

should we fill *error with a new error type an return nil from -(id)initWithJSONDictionary better than an Exception? or step over that key and continue with no error?

@jspahrsummers

I mean that we shouldn't be executing code which will cause NSDictionary to throw an exception.

Instead, we should follow a generalized version of these steps:

  1. Break apart nested.name into two components: nested and name
  2. Retrieve nested from the dictionary. If it's not a dictionary itself, error out.
  3. Retrieve name from the nested dictionary.
@dcordero

Modified fix to avoid sending an exception an returning an error instead

@jspahrsummers jspahrsummers self-assigned this
@jspahrsummers jspahrsummers merged commit cf3bb6e into from
@jspahrsummers

Thank you! I made some minor formatting changes in df03aa9, but otherwise this looks great! :star2:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 17, 2013
  1. Add podspec file

    David Cordero authored
Commits on Feb 4, 2014
  1. Merge remote-tracking branch 'upstream/master'

    David Cordero authored
  2. Fix a crash when an invalid keypath is used

    David Cordero authored
  3. Revert "Add podspec file"

    David Cordero authored
    This reverts commit 22712f9863a01091ad9f6b0bb186382204f8583e.
Commits on Feb 6, 2014
  1. @dcordero
This page is out of date. Refresh to see the latest.
Showing with 34 additions and 1 deletion.
  1. +18 −1 Mantle/MTLJSONAdapter.m
  2. +16 −0 MantleTests/MTLJSONAdapterSpec.m
View
19 Mantle/MTLJSONAdapter.m
@@ -106,7 +106,24 @@ - (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)mo
NSString *JSONKeyPath = [self JSONKeyPathForPropertyKey:propertyKey];
if (JSONKeyPath == nil) continue;
- id value = [JSONDictionary valueForKeyPath:JSONKeyPath];
+ id value = JSONDictionary;
+ NSArray *JSONKeyPathComponents = [JSONKeyPath componentsSeparatedByString:@"."];
+ for (NSString *itemJSONKeyPathComponent in JSONKeyPathComponents) {
+ if (![value isKindOfClass:NSDictionary.class]) {
+ if (error != NULL) {
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid JSON dictionary", @""),
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%@ could not be created because an invalid JSON dictionary was provided for keypath: %@", @""), NSStringFromClass(modelClass), JSONKeyPath],
+ };
+ *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo];
+ }
+ return nil;
+ }
+
+ value = [value valueForKey:itemJSONKeyPathComponent];
+ if (value == nil) break;
+ }
+
if (value == nil) continue;
@try {
View
16 MantleTests/MTLJSONAdapterSpec.m
@@ -73,6 +73,22 @@
expect([MTLJSONAdapter JSONDictionaryFromModel:model]).to.equal(values);
});
+it(@"should return nil and error with an invalid key path from JSON",^{
+
+ NSDictionary *values = @{
+ @"username": @"foo",
+ @"nested": @"bar",
+ @"count": @"0"
+ };
+
+ NSError *error = nil;
+ MTLTestModel *model = [MTLJSONAdapter modelOfClass:MTLTestModel.class fromJSONDictionary:values error:&error];
+ expect(model).beNil();
+ expect(error).notTo.beNil();
+ expect(error.domain).to.equal(MTLJSONAdapterErrorDomain);
+ expect(error.code).to.equal(MTLJSONAdapterErrorInvalidJSONDictionary);
+});
+
it(@"should return nil and an error with a nil JSON dictionary", ^{
NSError *error = nil;
MTLJSONAdapter *adapter = [[MTLJSONAdapter alloc] initWithJSONDictionary:nil modelClass:MTLTestModel.class error:&error];
Something went wrong with that request. Please try again.