CoderMJLee / MJExtension

A fast, convenient and nonintrusive conversion framework between JSON and model. Your model class doesn't need to extend any base class. You don't need to modify any model file.
MIT License
8.52k stars 2.16k forks source link
array dictionary json model objective-c

MJExtension

SPM supported Carthage compatible podversion Platform

📜✍🏻Release Notes: more details

Contents


Getting Started【开始使用】

Features【能做什么】

Installation【安装】

CocoaPods【使用CocoaPods】

pod 'MJExtension'

Carthage

github "CoderMJLee/MJExtension"

Swift Package Manager

Released from 3.4.0

Manually【手动导入】

Examples【示例】

Add MJKeyValue protocol to your model if needed【如果有需要, 请在模型中加入 MJKeyValue 协议】

Usage in Swift [关于在Swift中使用MJExtension] ‼️

Example:

@objc(MJTester)
@objcMembers
class MJTester: NSObject {
    // make sure to use `dynamic` attribute for basic type & must use as Non-Optional & must set initial value
    dynamic var isSpecialAgent: Bool = false
    dynamic var age: Int = 0

    var name: String?
    var identifier: String?
}
  1. @objc or @objcMembers attributes should be added to class or property for declaration of Objc accessibility [在 Swift4 之后, 请在属性前加 @objc 修饰或在类前增加 @objcMembers. 以保证 Swift 的属性能够暴露给 Objc 使用. ]
  2. If you let Bool & Int as property type, make sure that using dynamic to attribute it. It must be Non-Optional type and assign a default value.[如果要使用 BoolInt 等 Swfit 专用基本类型, 请使用 dynamic 关键字修饰, 类型为 Non-Optional, 並且给定初始值.]

纯Swift版的JSON与Model转换框架已经开源上架

  • KakaJSON
  • 中文教程
  • 如果你的项目是用Swift写的Model,墙裂推荐使用KakaJSON
    • 已经对各种常用的数据场景进行了大量的单元测试
    • 简单易用、功能丰富、转换快速

The most simple JSON -> Model【最简单的字典转模型】

typedef enum {
    SexMale,
    SexFemale
} Sex;

@interface User : NSObject
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *icon;
@property (assign, nonatomic) unsigned int age;
@property (copy, nonatomic) NSString *height;
@property (strong, nonatomic) NSNumber *money;
@property (assign, nonatomic) Sex sex;
@property (assign, nonatomic, getter=isGay) BOOL gay;
@end

/***********************************************/

NSDictionary *dict = @{
    @"name" : @"Jack",
    @"icon" : @"lufy.png",
    @"age" : @20,
    @"height" : @"1.55",
    @"money" : @100.9,
    @"sex" : @(SexFemale),
    @"gay" : @"true"
//   @"gay" : @"1"
//   @"gay" : @"NO"
};

// JSON -> User
User *user = [User mj_objectWithKeyValues:dict];

NSLog(@"name=%@, icon=%@, age=%zd, height=%@, money=%@, sex=%d, gay=%d", user.name, user.icon, user.age, user.height, user.money, user.sex, user.gay);
// name=Jack, icon=lufy.png, age=20, height=1.550000, money=100.9, sex=1

JSONString -> Model【JSON字符串转模型】

// 1.Define a JSONString
NSString *jsonString = @"{\"name\":\"Jack\", \"icon\":\"lufy.png\", \"age\":20}";

// 2.JSONString -> User
User *user = [User mj_objectWithKeyValues:jsonString];

// 3.Print user's properties
NSLog(@"name=%@, icon=%@, age=%d", user.name, user.icon, user.age);
// name=Jack, icon=lufy.png, age=20

Model contains model【模型中嵌套模型】

@interface Status : NSObject
@property (copy, nonatomic) NSString *text;
@property (strong, nonatomic) User *user;
@property (strong, nonatomic) Status *retweetedStatus;
@end

/***********************************************/

NSDictionary *dict = @{
    @"text" : @"Agree!Nice weather!",
    @"user" : @{
        @"name" : @"Jack",
        @"icon" : @"lufy.png"
    },
    @"retweetedStatus" : @{
        @"text" : @"Nice weather!",
        @"user" : @{
            @"name" : @"Rose",
            @"icon" : @"nami.png"
        }
    }
};

// JSON -> Status
Status *status = [Status mj_objectWithKeyValues:dict];

NSString *text = status.text;
NSString *name = status.user.name;
NSString *icon = status.user.icon;
NSLog(@"text=%@, name=%@, icon=%@", text, name, icon);
// text=Agree!Nice weather!, name=Jack, icon=lufy.png

NSString *text2 = status.retweetedStatus.text;
NSString *name2 = status.retweetedStatus.user.name;
NSString *icon2 = status.retweetedStatus.user.icon;
NSLog(@"text2=%@, name2=%@, icon2=%@", text2, name2, icon2);
// text2=Nice weather!, name2=Rose, icon2=nami.png

Model contains model-array【模型中有个数组属性,数组里面又要装着其他模型】

@interface Ad : NSObject
@property (copy, nonatomic) NSString *image;
@property (copy, nonatomic) NSString *url;
@end

@interface StatusResult : NSObject
/** Contatins status model */
@property (strong, nonatomic) NSMutableArray *statuses;
/** Contatins ad model */
@property (strong, nonatomic) NSArray *ads;
@property (strong, nonatomic) NSNumber *totalNumber;
@end

/***********************************************/

// Tell MJExtension what type of model will be contained in statuses and ads.
[StatusResult mj_setupObjectClassInArray:^NSDictionary *{
    return @{
               @"statuses" : @"Status",
               // @"statuses" : [Status class],
               @"ads" : @"Ad"
               // @"ads" : [Ad class]
           };
}];
// Equals: StatusResult.m implements +mj_objectClassInArray method.

NSDictionary *dict = @{
    @"statuses" : @[
                      @{
                          @"text" : @"Nice weather!",
                          @"user" : @{
                              @"name" : @"Rose",
                              @"icon" : @"nami.png"
                          }
                      },
                      @{
                          @"text" : @"Go camping tomorrow!",
                          @"user" : @{
                              @"name" : @"Jack",
                              @"icon" : @"lufy.png"
                          }
                      }
                  ],
    @"ads" : @[
                 @{
                     @"image" : @"ad01.png",
                     @"url" : @"http://www.ad01.com"
                 },
                 @{
                     @"image" : @"ad02.png",
                     @"url" : @"http://www.ad02.com"
                 }
             ],
    @"totalNumber" : @"2014"
};

// JSON -> StatusResult
StatusResult *result = [StatusResult mj_objectWithKeyValues:dict];

NSLog(@"totalNumber=%@", result.totalNumber);
// totalNumber=2014

// Printing
for (Status *status in result.statuses) {
    NSString *text = status.text;
    NSString *name = status.user.name;
    NSString *icon = status.user.icon;
    NSLog(@"text=%@, name=%@, icon=%@", text, name, icon);
}
// text=Nice weather!, name=Rose, icon=nami.png
// text=Go camping tomorrow!, name=Jack, icon=lufy.png

// Printing
for (Ad *ad in result.ads) {
    NSLog(@"image=%@, url=%@", ad.image, ad.url);
}
// image=ad01.png, url=http://www.ad01.com
// image=ad02.png, url=http://www.ad02.com

Model name - JSON key mapping【模型中的属性名和字典中的key不相同(或者需要多级映射)】

@interface Bag : NSObject
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) double price;
@end

@interface Student : NSObject
@property (copy, nonatomic) NSString *ID;
@property (copy, nonatomic) NSString *desc;
@property (copy, nonatomic) NSString *nowName;
@property (copy, nonatomic) NSString *oldName;
@property (copy, nonatomic) NSString *nameChangedTime;
@property (strong, nonatomic) Bag *bag;
@end

/***********************************************/

// How to map
[Student mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
    return @{
               @"ID" : @"id",
               @"desc" : @"description",
               @"oldName" : @"name.oldName",
               @"nowName" : @"name.newName",
               @"nameChangedTime" : @"name.info[1].nameChangedTime",
               @"bag" : @"other.bag"
           };
}];
// Equals: Student.m implements +mj_replacedKeyFromPropertyName method.

NSDictionary *dict = @{
    @"id" : @"20",
    @"description" : @"kids",
    @"name" : @{
        @"newName" : @"lufy",
        @"oldName" : @"kitty",
        @"info" : @[
                 @"test-data",
                 @{
                             @"nameChangedTime" : @"2013-08"
                         }
                  ]
    },
    @"other" : @{
        @"bag" : @{
            @"name" : @"a red bag",
            @"price" : @100.7
        }
    }
};

// JSON -> Student
Student *stu = [Student mj_objectWithKeyValues:dict];

// Printing
NSLog(@"ID=%@, desc=%@, oldName=%@, nowName=%@, nameChangedTime=%@",
      stu.ID, stu.desc, stu.oldName, stu.nowName, stu.nameChangedTime);
// ID=20, desc=kids, oldName=kitty, nowName=lufy, nameChangedTime=2013-08
NSLog(@"bagName=%@, bagPrice=%f", stu.bag.name, stu.bag.price);
// bagName=a red bag, bagPrice=100.700000

JSON array -> model array【将一个字典数组转成模型数组】

NSArray *dictArray = @[
                         @{
                             @"name" : @"Jack",
                             @"icon" : @"lufy.png"
                         },
                         @{
                             @"name" : @"Rose",
                             @"icon" : @"nami.png"
                         }
                     ];

// JSON array -> User array
NSArray *userArray = [User mj_objectArrayWithKeyValuesArray:dictArray];

// Printing
for (User *user in userArray) {
    NSLog(@"name=%@, icon=%@", user.name, user.icon);
}
// name=Jack, icon=lufy.png
// name=Rose, icon=nami.png

Model -> JSON【将一个模型转成字典】

// New model
User *user = [[User alloc] init];
user.name = @"Jack";
user.icon = @"lufy.png";

Status *status = [[Status alloc] init];
status.user = user;
status.text = @"Nice mood!";

// Status -> JSON
NSDictionary *statusDict = status.mj_keyValues;
NSLog(@"%@", statusDict);
/*
 {
 text = "Nice mood!";
 user =     {
 icon = "lufy.png";
 name = Jack;
 };
 }
 */

// More complex situation
Student *stu = [[Student alloc] init];
stu.ID = @"123";
stu.oldName = @"rose";
stu.nowName = @"jack";
stu.desc = @"handsome";
stu.nameChangedTime = @"2018-09-08";

Bag *bag = [[Bag alloc] init];
bag.name = @"a red bag";
bag.price = 205;
stu.bag = bag;

NSDictionary *stuDict = stu.mj_keyValues;
NSLog(@"%@", stuDict);
/*
{
    ID = 123;
    bag =     {
        name = "\U5c0f\U4e66\U5305";
        price = 205;
    };
    desc = handsome;
    nameChangedTime = "2018-09-08";
    nowName = jack;
    oldName = rose;
}
 */

Model array -> JSON array【将一个模型数组转成字典数组】

// New model array
User *user1 = [[User alloc] init];
user1.name = @"Jack";
user1.icon = @"lufy.png";

User *user2 = [[User alloc] init];
user2.name = @"Rose";
user2.icon = @"nami.png";

NSArray *userArray = @[user1, user2];

// Model array -> JSON array
NSArray *dictArray = [User mj_keyValuesArrayWithObjectArray:userArray];
NSLog(@"%@", dictArray);
/*
 (
 {
 icon = "lufy.png";
 name = Jack;
 },
 {
 icon = "nami.png";
 name = Rose;
 }
 )
 */

Core Data

func json2CoreDataObject() {
    context.performAndWait {
        let object = MJCoreDataTester.mj_object(withKeyValues: Values.testJSONObject, context: context)
        // use the object
    }
}

func coreDataObject2JSON() {
    context.performAndWait {        
        let dict = coreDataObject.mj_keyValues()
        // use dict
    }
}

Coding (Archive & Unarchive methods are deprecated in iOS 12)

#import "MJExtension.h"

@implementation MJBag
// NSCoding Implementation
MJCodingImplementation
@end

/***********************************************/

// what properties not to be coded
[MJBag mj_setupIgnoredCodingPropertyNames:^NSArray *{
    return @[@"name"];
}];
// Equals: MJBag.m implements +mj_ignoredCodingPropertyNames method.

// Create model
MJBag *bag = [[MJBag alloc] init];
bag.name = @"Red bag";
bag.price = 200.8;

NSString *file = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop/bag.data"];
// Encoding by archiving
[NSKeyedArchiver archiveRootObject:bag toFile:file];

// Decoding by unarchiving
MJBag *decodedBag = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
NSLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price);
// name=(null), price=200.800000

Secure Coding

Using MJSecureCodingImplementation(class, isSupport) macro.

@import MJExtension;

// NSSecureCoding Implementation
MJSecureCodingImplementation(MJBag, YES)

@implementation MJBag
@end

 /***********************************************/

// what properties not to be coded
[MJBag mj_setupIgnoredCodingPropertyNames:^NSArray *{
    return @[@"name"];
}];
// Equals: MJBag.m implements +mj_ignoredCodingPropertyNames method.

// Create model
MJBag *bag = [[MJBag alloc] init];
bag.name = @"Red bag";
bag.price = 200.8;
bag.isBig = YES;
bag.weight = 200;

NSString *file = [NSTemporaryDirectory() stringByAppendingPathComponent:@"bag.data"];

NSError *error = nil;
// Encoding by archiving
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:bag requiringSecureCoding:YES error:&error];
[data writeToFile:file atomically:true];

// Decoding by unarchiving
NSData *readData = [NSFileManager.defaultManager contentsAtPath:file];
error = nil;
MJBag *decodedBag = [NSKeyedUnarchiver unarchivedObjectOfClass:MJBag.class fromData:readData error:&error];
MJExtensionLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price);

Camel -> underline【统一转换属性名(比如驼峰转下划线)】

// Dog
#import "MJExtension.h"

@implementation Dog
+ (NSString *)mj_replacedKeyFromPropertyName121:(NSString *)propertyName
{
    // nickName -> nick_name
    return [propertyName mj_underlineFromCamel];
}
@end

// NSDictionary
NSDictionary *dict = @{
                       @"nick_name" : @"旺财",
                       @"sale_price" : @"10.5",
                       @"run_speed" : @"100.9"
                       };
// NSDictionary -> Dog
Dog *dog = [Dog mj_objectWithKeyValues:dict];

// printing
NSLog(@"nickName=%@, scalePrice=%f runSpeed=%f", dog.nickName, dog.salePrice, dog.runSpeed);

NSString -> NSDate, nil -> @""【过滤字典的值(比如字符串日期处理为NSDate、字符串nil处理为@"")】

// Book
#import "MJExtension.h"

@implementation Book
- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property
{
    if ([property.name isEqualToString:@"publisher"]) {
        if (oldValue == nil) return @"";
    } else if (property.type.typeClass == [NSDate class]) {
        NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
        fmt.dateFormat = @"yyyy-MM-dd";
        return [fmt dateFromString:oldValue];
    }

    return oldValue;
}
@end

// NSDictionary
NSDictionary *dict = @{
                       @"name" : @"5分钟突破iOS开发",
                       @"publishedTime" : @"2011-09-10"
                       };
// NSDictionary -> Book
Book *book = [Book mj_objectWithKeyValues:dict];

// printing
NSLog(@"name=%@, publisher=%@, publishedTime=%@", book.name, book.publisher, book.publishedTime);

NSDate -> NSString【模型转字典时, 修改 Date 类型至 String】

- (void)mj_objectDidConvertToKeyValues:(NSMutableDictionary *)keyValues {
    // NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    // formatter.dateFormat = @"yyy-MM-dd";
    // should use sharedFormatter for better performance  
    keyValues[@"publishedTime"] = [sharedFormatter stringFromDate:self.publishedTime];
}

More use cases【更多用法】

期待