Easy JSON to NSObject mapping using Cocoa's key value coding (KVC)

Related tags

JSON Motis
Overview

Version Platform Build Status CocoaDocs

Mobile Jazz Motis

#Motis Object Mapping

Easy JSON to NSObject mapping using Cocoa's key value coding (KVC)

Motis is a user-friendly interface with Key Value Coding that provides your NSObjects tools to map key-values stored in dictionaries into themselves. With Motis your objects will be responsible for each mapping (distributed mapping definitions) and you won't have to worry for data validation, as Motis will validate your object types for you.

Check our blog post entry Using KVC to parse JSON to get a deeper idea of Motis foundations.

How To Get Motis

If you use CocoaPods, you can get Motis by adding to your podfile:

pod 'Motis', '~>1.4.0'

Using Motis

  1. Import the file #import <Motis/Motis.h>
  2. Setup your motis objects (see below).
  3. Get some JSON to be mapped in your model objects.
  4. Call mts_setValuesForKeysWithDictionary: on your model object, using as argument the JSON dictionary to be mapped.
- (void)motisTest
{
	// Some JSON object
	NSDictionary *jsonObject = [...];

	// Creating an instance of your class
	MyClass instance = [[MyClass alloc] init];

	// Parsing and setting the values of the JSON object
	[instance mts_setValuesForKeysWithDictionary:jsonObject];
}

Setup Motis Objects

1. Define the motis mapping dictionary

Your custom object (subclass of NSObject) needs to override the method +mts_mapping and define the mappging from the JSON keys to the Objective-C property names.

For example, if receiving the following JSON:

{
  {
    "user_name" : "john.doe",
    "user_id" : 42,
    "creation_date" :  "1979-11-07 17:23:51",
    "webiste" : "http://www.domain.com",
    "user_stats" : {
                     "views" : 431,
                     "ranking" : 12,
                   },
    "user_avatars": [{
      "avatar_type": "standard",
      "image_url": "http://www.avatars.com/john.doe"
    }, {
      "avatar_type": "large",
      "image_url": "http://www.avatars.com/john.doe/large"
    }]
  }
}

Then, in our User class entity (NSObject subclass) we would define the +mts_mapping method as follows:

// --- User.h --- //

@interface User : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSIntger userId;
@property (nonatomic, strong) NSDate *creationDate;
@property (nonatomic, strong) NSURL *website;
@property (nonatomic, assign) NSInteger views;
@property (nonatomic, assign) NSInteger ranking;
@property (nonatomic, assign) NSURL *avatar;

@end

// --- User.m --- //

@implementation User

+ (NSDictionary*)mts_mapping
{
    return @{@"user_name": mts_key(name),
             @"user_id": mts_key(userId),
             @"creation_date": mts_key(creationDate),
             @"website": mts_key(website),
             @"user_stats.views": mts_key(views),  // <-- KeyPath access
             @"user_stats.ranking": mts_key(ranking), // <-- KeyPath access
             @"user_avatars.0.image_url": mts_key(avatar) // <-- KeyPath access using array index
            };
}

@end

As shown in the example above, KeyPath access is supported to reference JSON content of dictionaries. KeyPath access supports array index lookups when you need to map values into properties using a fixed array index.

Also, as you might see, we are specifying custom types as NSDate or NSURL. Motis automatically attempt to convert JSON values to the object defined types. Check below for more information on automatic validation.

1.1 Mapping Filtering

By default, Motis attempt to set via KVC any property name. Therefore, even if you don't define a mapping in the method +mts_mapping: but your JSON dictionary contains keys that match the name of your object properties, motis will assign and validate those values.

This might be problematic if you have no control over your JSON dictionaries. Therefore, Motis will restrict the accepted mapping keys to the ones defined in the Motis mapping. You can change this behaviour by overriding the method +mts_shouldSetUndefinedKeys.

+ (BOOL)mts_shouldSetUndefinedKeys
{
    // By default this method return NO unless you haven't defined a mapping.
    // You can override it and return YES to only accept any key.
    return NO;
}

1.2 Value mapping

With the method +mts_valueMappingForKey: objects can define value mappings. This is very useful when a string value has to be mapped into a enum for example. Check the following example on how to implement this method:

For the following JSON...

{
  {
    "user_name" : "john.doe",
    "user_gender": "male",
  }
}

...we define the following class and Motis behaviour:

typedef NS_ENUM(NSUInteger, MJUserGender)
{
    MJUserGenderUndefined,
    MJUserGenderMale,
    MJUserGenderFemale,
};

@interface User : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assing) MJUserGender gender;
@end

@implementation User
+ (NSDictionary*)mts_mapping
{
    return @{"user_name": mts_key(name),
             "user_gender": mts_key(gender),
            };
}

+ (NSDictionary*)mts_valueMappingForKey:(NSString*)key
{
    if ([key isEqualToString:mts_key(gender)])
    {
        return @{"male": @(MJUserGenderMale),
                 "female": @(MJUserGenderFemale),
                 MTSDefaultValue: @(MJUserGenderUndefined),
                };
    }
    return nil;
}
@end

The above code will automatically translate the "male"/"female" values into the enum MJUserGender. Optionally, by using the MTSDefaultValue key, we can specify a default value when a value is not contained in the dictionary or is null. If we don't define the MTSDefalutValue, then Motis will attempt to set the original received value.

2. Value Validation

2.1 Automatic Validation

Motis checks the object type of your values when mapping from a JSON response. The system will try to fit your defined property types (making introspection in your classes). JSON objects contains only strings, numbers, dictionaries and arrays. Automatic validation will try to convert from these types to your property types. If cannot be achieved, the mapping of those property won't be performed and the value won't be set.

For example, if you specify a property type as a NSURL and your JSON is sending a string value, Motis will convert the string into a NSURL automatically. However, if your property type is a "UIImage" and your json is sending a number, Motis will ignore the value and won't set it. Automatic validation done for types as NSURL, NSData, NSDate, NSNumber, and a few more. For more information check the table on the bottom of this file.

Automatic validation will be performed always unless the user is validating manually, thus in this case the user will be responsible of validating correctly the values.

2.1.1 Automatic Date Validation

Whenever you specify a property type as a NSDate, Motis will attempt to convert the received JSON value in to a date.

  1. If the JSON value is a number or an string containing a number, Motis will create the date considering that the number is the number of seconds elapsed since 1979-1-1.
  2. If the JSON value is a string, Motis will use the NSDateFormatter returned by the method +mts_validationDateFormatter. Motis does not implement any defautl date formatter (therefore, this method return nil by default). It is your responsibility to provide a default date formatter.

However, if you have multiple formats for different keys, you will have to validate your dates using manual validation (see below).

2.1.2 Automatic Array Validation

In order to support automatic validation for array content (objects inside of an array), you must override the method +mts_arrayClassMapping and return a dictionary containing pairs of array property name and class type for its content.

For example, having the following JSON:

{
  "user_name": "john.doe",
  "user_id" : 42,
  ...
  "user_followers": [
                      {
                        "user_name": "william",
                        "user_id": 55,
                        ...
                      },
                      {
                        "user_name": "jenny",
                        "user_id": 14,
                        ...
                      },
                      ...
                    ]
}

Therefore, our User class has an NSArray property called followers that contain a list of objects of type User, we would override the method and implement it as it follows:

@implementation User

+ (NSDictionary*)mts_mapping
{
    return @{@"user_name": mts_key(name),
             @"user_id": mts_key(userId),
             ...
             @"user_followers": mts_key(followers),
            };
}

+ (NSDictionary*)mts_arrayClassMapping
{
    return @{mts_key(followers): User.class};
}

@end
2.1.3 Automatic Object Creation

When validating autmatically, Motis might attempt to create new instances of your custom objects. For example, if a JSON value is a dictionary and the key-associated property type is a custom object, Motis will try to create recursively a new object of the corresponding type and set it via -mts_setValuesForKeysWithDictionary:.

This automatic "object creation", that is also done for contents of an array, can be customized and tracked by using the following two methods: one "will"-styled method to notify the user that an object will be created and one "did"-styled method to notify the user that an object has been created.

@implementation User

- (id)mts_willCreateObjectOfClass:(Class)typeClass withDictionary:(NSDictionary*)dictionary forKey:(NSString*)key abort:(BOOL*)abort
{
    // Return "nil" if you want Motis to handle the object creation and mapping.
    // Otherwise, create/reuse an object of the given "typeClass" and map the values from the dictionary and return it.
    // If you set "abort" to yes, the value for the given "key" won't be set.

    // This method is also used for array contents. In this case, "key" will be the name of the array.
}

- (void)mts_didCreateObject:(id)object forKey:(NSString *)key
{
    // Motis notifies you the new created object.
    // This method is also used for array contents. In this case, "key" will be the name of the array.
}

@end

2.2 Manual Validation

Manual validation is performed before automatic validation and gives the user the oportunity of manually validate a value before any automatic validation is done. Of course, if the user validates manually a value for a given Key, any automatic validation will be performed.

Motis calls the following method to fire manual validation:

- (BOOL)mts_validateValue:(inout __autoreleasing id *)ioValue forKey:(NSString *)inKey error:(out NSError *__autoreleasing *)outError;

The default implementation of this method fires the KVC validation pattern. To manually validate a value you can implement the KVC validation method for the corresponding key:

- (BOOL)validate<Key>:(id *)ioValue error:(NSError * __autoreleasing *)outError
{
	// Check *ioValue and assign new value to ioValue if needed.
	// Return YES if *ioValue can be assigned to the attribute, NO otherwise
	return YES;
}

However, you can also override the Motis manual validation method listed above and perform your custom manual validation (without using the KVC validation pattern).

2.2.1 Manual Array Validation

You can also perform manual validation on array content. When mapping an array, motis will attempt to validate array content for the type define in the method +mts_arrayClassMapping. However, you can perform the validation manually if you prefer. By validating manually, automatic validation won't be performed.

To manually validate array content you must override the following method:

- (BOOL)mts_validateArrayObject:(inout __autoreleasing id *)ioValue forArrayKey:(NSString *)arrayKey error:(out NSError *__autoreleasing *)outError;
{
	// Check *ioValue and assign new value to ioValue if needed.
	// Return YES if *ioValue can be included into the array, NO otherwise

	return YES;
}

2.3 Adding Custom Actions After Mapping Finished

Custom validations can be added after Motis finishes permorming the mapping. To implement it, override the mts_setValuesForKeysWithDictionary: method in your custom class.

In the example below, we are mapping the JSON dictionary:

{
    "a": 2,
    "b": 5
}

to the following object:

@interface MyCustomObject : NSObject
@property (nonatomic, assign) NSInteger a; // JSON property
@property (nonatomic, assign) NSInteger b; // JSON property
@property (nonatomic, assign) NSInteger c; // Computed property
@end

@implementation MyCustomObject
- (void) mts_setValuesForKeysWithDictionary:(NSDictionary*)dictionary
{
   // Call the Motis mapping method
   [super mts_setValuesForKeysWithDictionary:dictionary];
 
    // After mapping has finished, call the validation method
    [self pfx_objectDidFinishMapping];
}
 
- (void)pfx_objectDidFinishMapping
{
    // Perform validations after mapping is finished.
    self.c = self.a + self.b;
}
@end

Furthermore, you can create an generic method in your model superclass to then heritate it in all the subclasses.

The Motis Object

You can subclass MTSMotisObject to obtain an object that automatically implements NSCoding and NSCopying by reusing Motis definitions.

Mainly, MTSMotisObject uses all defined properties inside the +mts_mapping dictionaries to implement NSCopying and NSCoding. Properties are get/set via Key-Value-Coding.

For example, if we have the following implementation:

@interface User : MTSMotisObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSDate *birthday;
@property (nonatomic, strong) NSURL *webiste;

@end

@implementation MTSMotisObject

+ (NSDictionary*)mts_mapping
{
	return @{@"user_name": mts_key(name),
	     	 @"birth_day": mts_key(birthday),
	         @"website_url": mts_key(website),
	        };
}

@end

Then we can do as follows:

- (void)foo
{
    User *user1 = [[User alloc] init];
    user1.name = @"John Doe";
    user1.birthday = [NSDate date];
    user1.website = [NSURL URLWithString:@"http://www.google.com"];

    // Create a copy of user1
    User *user2 = [user1 copy];

    // Transform user into NSData
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:user1];

    // Transform the data into a User instance
    User *user3 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}

Subclasses can also configure the list of property names by overriding the method +mts_motisPropertyNames and returning a different subset of property names. By default this method returns the Motis-defined property names.


Appendix

Thread Safety

Starting at version 1.0.2, Motis can be used simultaneously in multiple threads (is thread safe).

To understand why Motis had this problem, we need to know that Motis cache Objective-C runtime information to increase efficiency. This caching is done using NSMapTable and NSMutableDictionary instances and these objects are not thread safe while reading and editing its content, causing Motis to fail in thread safety.

However, it is important to understand that at the very end, Motis is using KVC to access and manipulate objects. Therefore, it is the developer responsibility to make object getters and setters thread safe, otherwise Motis won't be able to make it for you.

Motis & Core Data

If your project is using CoreData and you are dealing with NSManagedObject instances, you can still using Motis to map JSON values into your class instances. However, you will have to take care of a few things:

####1. Don't use KVC validation

CoreData uses KVC validation to validate NSManagedObject properties when performing a -saveContext: action. Therefore, you must not use KVC validation to check the integrity and consistency of your JSON values. Consequently, you must override the Motis manual validation method in order to not perform KVC validation.

- (BOOL)mts_validateValue:(inout __autoreleasing id *)ioValue forKey:(NSString *)inKey error:(out NSError *__autoreleasing *)outError
{
    // Do manual validation for the given "inKey"
    return YES;
}

2. Help Motis create new instances

When parsing the JSON content into an object, Motis might find NSDictionay instances that might be converted into new model object instances. By default Motis, doing introspection, creates a new instnace of the corresponding class type and maps the values contained in the found dictionary recursively. However, when using CoreData you must allocate and initialize NSManagedObject instances providing a NSManagedObjectContext.

Therefore, you must override the method - (id)mts_willCreateObjectOfClass:(Class)typeClass withDictionary:(NSDictionary*)dictionary forKey:(NSString*)key abort:(BOOL*)abort and return an instnace of typeClass having performed Motis with the dictionary.

For example, we could create a new managed object for the corresponding class, perform Motis and return:

- (id)mts_willCreateObjectOfClass:(Class)typeClass withDictionary:(NSDictionary*)dictionary forKey:(NSString*)key abort:(BOOL*)abort
{
    if ([typeClass isSubclassOfClass:NSManagedObject.class])
    {
        // Get the entityName
        NSString *entityName = [typeClass entityName]; // <-- This is a custom method that returns the entity name

        // Create a new managed object for the given class (for example).
        NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entityName inManagedObjectContext:self.context];
        NSManagedObject *object = [[typeClass alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:self.context];

        // Perform Motis on the object instance
        [object mts_setValuesForKeysWithDictionary:dictionary];

        return object;
    }
    return nil;
}

3. Enable equality check

In order to avoid unnecessary "hasChanges" flags in our managed objects, override the method -mts_checkValueEqualityBeforeAssignmentForKey: and return YES in order to make Motis check equality before assigning a value via KVC. This way, if a new value is equal (via the isEqual: method) to the current already set value, Motis will not make the assigniment.

- (BOOL)mts_checkValueEqualityBeforeAssignmentForKey:(NSString*)key
{
   // Return YES to make Motis check for equality before making an assignment via KVC.
   // Return NO to make Motis always assign a value via KVC without checking for equality before.

   return YES;
}

Automatic Validation

The following table indicates the supported validations in the current Motis version:

 +------------+---------------------+------------------------------------------------------------------------------------+
 | JSON Type  | Property Type       | Comments                                                                           |
 +------------+---------------------+------------------------------------------------------------------------------------+
 | string     | NSString            | No validation is requried                                                          |
 | number     | NSNumber            | No validation is requried                                                          |
 | number     | basic type (1)      | No validation is requried                                                          |
 | array      | NSArray             | No validation is requried                                                          |
 | dictionary | NSDictionary        | No validation is requried                                                          |
 | -          | -                   | -                                                                                  |
 | string     | bool                | string parsed with method -boolValue and by comparing with "true" and "false"      |
 | string     | unsigned long long  | string parsed with NSNumberFormatter (allowFloats disabled)                        |
 | string     | basic types (2)     | value generated automatically by KVC (NSString's '-intValue', '-longValue', etc)   |
 | string     | NSNumber            | string parsed with method -doubleValue                                             |
 | string     | NSURL               | created using [NSURL URLWithString:]                                               |
 | string     | NSData              | attempt to decode base64 encoded string                                            |
 | string     | NSDate              | default date format "2011-08-23 10:52:00". Check '+mts_validationDateFormatter.'   |
 | -          | -                   | -                                                                                  |
 | number     | NSDate              | timestamp since 1970                                                               |
 | number     | NSString            | string by calling NSNumber's '-stringValue'                                        |
 | -          | -                   | -                                                                                  |
 | array      | NSMutableArray      | creating new instance from original array                                          |
 | array      | NSSet               | creating new instance from original array                                          |
 | array      | NSMutableSet        | creating new instance from original array                                          |
 | array      | NSOrderedSet        | creating new instance from original array                                          |
 | array      | NSMutableOrderedSet | creating new instance from original array                                          |
 | -          | -                   | -                                                                                  |
 | dictionary | NSMutableDictionary | creating new instance from original dictionary                                     |
 | dictionary | custom NSObject     | Motis recursive call. Check '-mts_willCreateObject..' and '-mtd_didCreateObject:'  |
 | -          | -                   | -                                                                                  |
 | null       | nil                 | if property is type object                                                         |
 | null       | <UNDEFINED>         | if property is basic type (3). Check KVC method '-setNilValueForKey:'              |
 +------------+---------------------+------------------------------------------------------------------------------------+

* basic type (1) : int, unsigned int, long, unsigned long, long long, unsigned long long, float, double)
* basic type (2) : int, unsigned int, long, unsigned long, float, double)
* basic type (3) : any basic type (non-object type).

Project Maintainer

This open source project is maintained by Joan Martin.

License

Copyright 2016 Mobile Jazz

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • ¿Bug mapping array?

    ¿Bug mapping array?

    So, I’ve got something like this:

    @interface ObjectList : NSObject
    
    @property (nonatomic, assign) NSArray* items;
    
    @end
    
    @implementation ObjectList
    
    + (NSDictionary*)mts_mapping
    {
        return @{
            @"items" : mts_key(items),
        };
    }
    
    + (NSDictionary*)mts_arrayClassMapping
    {
        return @{
            mts_key(items) : Object.class,
        };
    }
    
    @end
    

    Motis seems to be unable to create de Object items of the items array (results in empty array), even though it does fine with a single Object alone, which contains several properties of NSNumber, NSString, NSArray types and a custom one that is also working with single items.

    I’m pretty lost here; may you help me out pointing something that I might be missing?

    opened by rubenillodo 7
  • mts_setValuesForKeysWithDictionary not thread-safe

    mts_setValuesForKeysWithDictionary not thread-safe

    I've been seeing occasional crashes "error for object 0x7fbf1b42bbb0: pointer being freed was not allocated" in places like the dictionary[typeAttribute] = isClassType; line of mts_isClassTypeTypeAttribute.

    Searching the web for NSMutableDictionary "pointer being freed was not allocated" found http://stackoverflow.com/a/16976032 warning about "mutating the dictionary's contents on different threads at the same time", and I see that our app is indeed calling mts_setValuesForKeysWithDictionary from within several occurrences of a dispatch_async(dispatch_get_global_queue(..) block.

    Can I get any tips for making mts_setValuesForKeysWithDictionary thread safe? I see several category methods in NSObject+Motis.m that use a static NSMutableDictionary to cache results for the same type, optimizations I can either disable or make thread-safe. Is there anything more subtle I also need to look at?

    opened by jpmhouston 6
  • Add option to preserve mutability when setting value to NSMutableArray properties

    Add option to preserve mutability when setting value to NSMutableArray properties

    How to replicate problem?

    1. Create Motis object with NSMutableArray property
    2. Set values with mts_setValuesForKeysWithDictionary
    3. In mts_validateAutomaticallyValue:toClass:forKey: array is parsed in first lines, in if ([*ioValue isKindOfClass:typeClass]) statement with return YES at the end.

    Now arrays are not parsed at the beginning, they go to the existing if/else statement where mutability is properly checked.

    opened by dstranz 5
  • Remove backspace character

    Remove backspace character

    Xcode 7 keeps crashing when we are editing code that use MTSMotisObject, because there is invalid backspace character in comment block.

    Application Specific Information:
    ProductBuildVersion: 7B1005
    ASSERTION FAILURE in /Library/Caches/com.apple.xbs/Sources/IDEPlugins/IDEPlugins-9057/IDEQuickHelp/Controllers/IDEQuickHelpController.m:360
    Details:  Error creating XML document from clang-parsed comment block: Error Domain=NSXMLParserErrorDomain Code=9 "Line 2: PCDATA invalid Char value 8
    " UserInfo={NSLocalizedDescription=Line 2: PCDATA invalid Char value 8
    }
    Object:   <IDEQuickHelpInspectorController: 0x7fe7187a7e50>
    Method:   -generateHTMLForSymbol:fromQueryDictionary:inExpressionSource:context:
    Thread:   <NSThread: 0x7fe712d19bc0>{number = 1, name = main}
    Hints:   None
    Backtrace:
      0  0x0000000106780d56 -[IDEAssertionHandler handleFailureInMethod:object:fileName:lineNumber:assertionSignature:messageFormat:arguments:] (in IDEKit)
    

    mtsmotisobject_h_ _search_results_____b___and_multi-file_search

    opened by dstranz 4
  • Avoid setting values when equal

    Avoid setting values when equal

    In some cases, it’s useful to avoid calling setters when the values are known to be equal. In particular, calling a setter on an NSManagedObject will flag that object as updated in the context, which will trigger a database write, even if [newValue isEqual:oldValue].

    This adds an optional -mts_checkValueOfKeyForEqualityBeforeAssignment:.

    opened by samkaufman 4
  • no kvc automatic conversion of string to basic type when overriding mts_validateValue

    no kvc automatic conversion of string to basic type when overriding mts_validateValue

    The automatic conversion of strings to basic type where "value is generated automatically by KVC" as described in the chart, that seems to not work when overriding mts_validateValue:forKey:error: for when objects are NSManagedObjects. Leads to the following exception throw from setValue:forKey:

    Description of exception being thrown: 'Unacceptable type of value for attribute: property = "latitude"; desired type = NSNumber; given type = __NSCFString; value = 0.01.'
    

    I was pleasantly surprised to find most of the Motis automatic conversions noted in the chart do get performed when mts_validateValue:forKey:error: is overridden, as first read of the documentation gave me the impression that it wouldn't. But I found this hiccup, and firstly I'd like to know if this is just one of several conversions that won't work for one reason or another when skipping the call to validateValue:forKey:error:.

    Because if it's just the doubles and other ints, except the ones with special cases in mts_validateAutomaticallyValue:forKey: like BOOL & unsigned long long, then secondly, why not add a few more cases to mts_validateAutomaticallyValue:forKey: for this situation? I've tried added cases for double, NSInteger & NSUInteger and it seems to avoid the exception noted above. I'll be submitting a pull request.

    opened by jpmhouston 3
  • Some odd  condition after issue #12

    Some odd condition after issue #12

    Example:

    source JSON

    NSDictionary *JSON = @{@"name" : @"shinrenpan" , @"user_age" : @(35)};
    

    User.h

    @interface User : NSObject
    @property (copy, nonatomic, readonly) NSString *name;
    @end
    

    Condition 1

    Did't implement both mts_shouldSetUndefinedKeys and implement +mts_mapping

    // User.m
    @implementation User
    @end
    
    User *user = [[User alloc]init];
    [user mts_setValuesForKeysWithDictionary:JSON]; // <-- Crash, because MOTIS try to set value to user_age, but User Class did't have property user_age
    NSLog(@"%@", user.name);
    

    Condition 2

    Only implement mts_shouldSetUndefinedKeys

    // User.m
    @implementation User
    + (BOOL)mts_shouldSetUndefinedKeys
    {
        return NO;
    }
    @end
    
    User *user = [[User alloc]init];
    [user mts_setValuesForKeysWithDictionary:JSON]; // <-- Won't Crash
    NSLog(@"%@", user.name); // <-- null, same issue as #12
    

    Condition 3

    Implment both implement + mts_shouldSetUndefinedKeys, implement +mts_mapping

    // User.m
    @implementation User
    +(NSDictionary *)mts_mapping
    {
        return @{@"name" : mts_key(name)};
    }
    
    + (BOOL)mts_shouldSetUndefinedKeys
    {
        return NO;
    }
    @end
    
    User *user = [[User alloc]init];
    [user mts_setValuesForKeysWithDictionary:JSON]; // <-- Won't Crash
    NSLog(@"%@", user.name); // <-- shinrenpan, but I need implement both + mts_shouldSetUndefinedKeys, implement +mts_mapping
    

    I have no time to review all code but I have fixed issue in gist quickly, you can compare it.

    It will works for Condition 1, 2, 3.

    opened by shinrenpan 3
  • Fix crash when inserting nil as KVC value

    Fix crash when inserting nil as KVC value

    When mapping the values, Motis' validator returns that nil is valid (and it might be the case somewhere) but when you are calling setValue:forKey: value can't be nil, so in this case we have to check for that too. If it gets through as nil it's simply going to crash the app.

    opened by R1ckye 3
  • Adding fixed array index property mapping

    Adding fixed array index property mapping

    I've encountered a few different scenarios where it's clumsy to create object classes as wrappers for elements in a JSON array when I'm only going to use one or two properties from a specific item in that array. I thought it'd be great if you could reference the array index directly in the keyPath string.

    With this pull request you can use an array index as part of the keyPath string. Given a structure like

    {"obj": [{
      "someKey": "someValue"
    },{
      "someKey": "anotherValue"
    }]}
    

    you could now get someValue into a property on your root object like this:

    @interface SomeObject : NSObject
    
    @property (nonatomic, strong) NSString *myval;
    
    @end
    
    @implementation SomeObject
    
    + (NSDictionary *)mts_mapping
    {
      return @{@"obj.0.someKey": mts_key(myval)};
    }
    @end
    

    Previously you'd have to create a new object and use that with mts_arrayClassMapping along with some additional code to pull the value into SomeObject.myval.

    opened by fikeminkel 2
  • Remote branch 1.4.0 not found in upstream origin

    Remote branch 1.4.0 not found in upstream origin

    Hi, today I updated my pod and then an error occurs, whether it is Motis own reasons?

    Logs

    ➜  iFamCare git:(develop) ✗ pod update
    Update all pods
    Analyzing dependencies
    Pre-downloading: `JLPermissions` from `[email protected]:VincentSit/JLPermissions.git`
    Downloading dependencies
    Installing AFNetworking 2.6.1 (was 2.6.0)
    Using AWSCore (2.2.7)
    Using AWSS3 (2.2.7)
    Using BlocksKit (2.2.5)
    Installing CocoaLumberjack 2.0.3 (was 2.0.1)
    Using DBPrivacyHelper (0.6.1)
    Using DZNEmptyDataSet (1.5.2)
    Using DateTools (1.7.0)
    Using FXPageControl (1.3.2)
    Using GBInfiniteScrollView (1.8)
    Installing INTULocationManager 4.1.1 (was 4.0.0)
    Installing JLPermissions 2.2.4 (was 2.2.4)
    Using MD5Digest (1.0.1)
    Using MagicalRecord (2.2)
    Using Masonry (0.6.3)
    Installing Motis 1.4.0 (was 1.3.0)
    
    [!] Error installing Motis
    [!] /usr/bin/git clone https://github.com/mobilejazz/Motis.git /var/folders/0b/50d9t8rd1xn4fmy2rlwrqy_40000gn/T/d20151021-29889-10pwm29 --single-branch --depth 1 --branch 1.4.0
    
    Cloning into '/var/folders/0b/50d9t8rd1xn4fmy2rlwrqy_40000gn/T/d20151021-29889-10pwm29'...
    warning: Could not find remote branch 1.4.0 to clone.
    fatal: Remote branch 1.4.0 not found in upstream origin
    
    opened by VincentSit 1
  • Deep NSObject to NSDictionary

    Deep NSObject to NSDictionary

    Hello, thanks for Motis. I use it in all my apps!

    I've got a question though:

    I noticed the method

    - (NSDictionary*)mts_dictionaryWithValuesForKeys:(NSArray *)keys
    

    doesn't do a 'deep' dictionary representation. What I mean is if you have an NSObject subclass such as Customer, and Customer has an array of Orders (Also an NSObject subclass). This method will return an array of Order instances as opposed to returning an array of Order dictionary representations.

    Similarly, if Customer has an Address property (Also an NSObject subclass) this method will return an Address instance as opposed to a dictionary representation of that address instance.

    Current output of:

    [customer mts_dictionaryWithValuesForKeys:[[[customer class] mts_mapping] allKeys]];
    

    is

    {"name":"Curtis","address":"<Address mem_address>","orders":["<Order mem_address>","<Order mem_address>"]}
    

    Desired output:

     {"name":"Curtis","address":{"street":"1111 Awesome Street","city":"You get the idea"},"orders": [{"id":1,"price":"5.00"},{"id":2,"price":"3.00"}]}
    

    Basically, I'm looking to do the standard Motis flow, but backwards.

    Is there a way to do this with the current motis version that I'm overlooking? Is it something you guys have though about including if not?

    • Thanks
    opened by cthorne91 1
Releases(1.4.3)
Owner
Mobile Jazz
Mobile Jazz
Reflection based (Dictionary, CKRecord, NSManagedObject, Realm, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift

EVReflection General information At this moment the master branch is tested with Swift 4.2 and 5.0 beta If you want to continue using EVReflection in

Edwin Vermeer 964 Dec 14, 2022
Simple JSON Object mapping written in Swift

ObjectMapper ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from J

Tristan Himmelman 9k Jan 2, 2023
JSONNeverDie - Auto reflection tool from JSON to Model, user friendly JSON encoder / decoder, aims to never die

JSONNeverDie is an auto reflection tool from JSON to Model, a user friendly JSON encoder / decoder, aims to never die. Also JSONNeverDie is a very important part of Pitaya.

John Lui 454 Oct 30, 2022
JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift.

JSEN /ˈdʒeɪsən/ JAY-sən JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift. A JSON, as defined in the EC

Roger Oba 8 Nov 22, 2022
JSON-Practice - JSON Practice With Swift

JSON Practice Vista creada con: Programmatic + AutoLayout Breve explicación de l

Vanesa Giselle Korbenfeld 0 Oct 29, 2021
Ss-json - High-performance json parsing in swift

json 0.1.1 swift-json is a pure-Swift JSON parsing library designed for high-per

kelvin 43 Dec 15, 2022
Swift-json - High-performance json parsing in swift

json 0.1.4 swift-json is a pure-Swift JSON parsing library designed for high-per

kelvin 43 Dec 15, 2022
Swift parser for JSON Feed — a new format similar to RSS and Atom but in JSON.

JSONFeed Swift parser for JSON Feed — a new format similar to RSS and Atom but in JSON. For more information about this new feed format visit: https:/

Toto Tvalavadze 31 Nov 22, 2021
Decodable Simple and strict, yet powerful object mapping made possible by Swift 2's error handling.

Decodable Simple and strict, yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by Argo, but without a bizillion

Johannes Lund 1k Jul 15, 2022
Property mapping for Objective-C iOS apps.

Stop repeating your data parsing code in iOS apps. Data parsing is one of most common tasks we need to do in our apps, yet still majority of people do

Krzysztof Zabłocki 1.1k Sep 8, 2022
A coding challenge done for Blinq

QuickText This is a coding challenge done for Blinq Launch Screen Add message screen Message List screen Edit Messsage screen Message Delete screen iM

null 0 Nov 28, 2021
Argo is a library that lets you extract models from JSON or similar structures in a way that's concise, type-safe, and easy to extend

Argo is a library that lets you extract models from JSON or similar structures in a way that's concise, type-safe, and easy to extend. Using Argo

thoughtbot, inc. 3.5k Dec 20, 2022
HandyJSON is a framework written in Swift which to make converting model objects to and from JSON easy on iOS.

HandyJSON To deal with crash on iOS 14 beta4 please try version 5.0.3-beta HandyJSON is a framework written in Swift which to make converting model ob

Alibaba 4.1k Dec 29, 2022
ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects to and from JSON.

ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from J

Tristan Himmelman 9k Jan 2, 2023
SwiftyJSON makes it easy to deal with JSON data in Swift.

SwiftyJSON SwiftyJSON makes it easy to deal with JSON data in Swift. Platform Build Status *OS Linux Why is the typical JSON handling in Swift NOT goo

SwiftyJSON 21.7k Jan 3, 2023
The easy to use Swift JSON decoder

Unbox is deprecated in favor of Swift’s built-in Codable API and the Codextended project. All current users are highly encouraged to migrate to Codable as soon as possible.

John Sundell 2k Dec 31, 2022
🌟 Super light and easy automatic JSON to model mapper

magic-mapper-swift ?? Super light and easy automatic JSON to model mapper Finish writing README.md Ability to map NSManagedObject Ability to convert m

Adrian Mateoaea 26 Oct 6, 2019
AlamofireObjectMappe - An Alamofire extension which converts JSON response data into swift objects using ObjectMapper

AlamofireObjectMapper An extension to Alamofire which automatically converts JSON response data into swift objects using ObjectMapper. Usage Given a U

Tristan Himmelman 2.6k Dec 29, 2022
YamlSwift - Load YAML and JSON documents using Swift

YamlSwift parses a string of YAML document(s) (or a JSON document) and returns a Yaml enum value representing that string.

Behrang Norouzinia 384 Nov 11, 2022