MAXJsonAdapter 0.1.7

MAXJsonAdapter 0.1.7

测试已测试
Lang语言 Obj-CObjective C
许可 MIT
发布最新发布2019年2月

Mathieu Grettir Skúlason 维护。



  • roodoodey

MAXJsonAdapter

CI Status Version License Platform

模型对象 - JSON 序列化/反序列化

在 Objective-C 中将模型对象从JSON序列化或反序列化到JSON需要很多样板代码。MAXJsonAdapter 负责序列化和反序列化模型对象,无需特定的子类,只需 NSObject(默认基本对象类)即可。

让我们考虑一个简单的用户模型接口和实现

@interface User : NSObject

@property (nonatomic, strong) NSString *email;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *middleName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *phoneNumber;

@property (nonatomic, strong) NSArray <NSString *> *usernames;

-(id)initWithJsonDict:(NSDictionary *)dict;

@end
@implementation User

-(id)initWithJsonDict:(NSDictionary *)dict {

    if(self = [super init]) {
    
     _email = [dict objectForKey:@"email"];
     _firstName = [dict objectForKey:@"firstName"];
     _middleName = [dict objectForKey:@"middleName"];
     _lastName = [dict objectForKey:@"lastName"];
     _phoneNumber = [dict objectForKey:@"phoneNumber"];
     
     _usernames = [dict objectForKey:@"usernames"];
     
    }
    
    return self;
}

@end

用户模型使用以下 json 数据序列化的字典初始化

{
    "email" : "[email protected]",
    "firstName" : "Bruce",
    "lastName" : "Wayne",
    "middleName": null,
    "phoneNumber" : "0018008608889",
    "usernames" : ["KillTheJoker", "RobinIsMyPet101", "TheRiddlerRiddlesTheRiddle"]
}

这是一个简化模型,其中没有日期或其他需要在运行时转换的值。对于此类行为,请参阅关于值转换器的章节。

我们不需要手动声明要从前面的字典中初始化哪些属性,可以使用 MAXJsonAdapter 来帮助我们,使用以下方法

User *user = [MAXJsonAdapter MAXJAObjectOfClass:[User class] delegate: nil fromDictionary: dict];

如果您已经有了一个模型对象并想使用一个字典来更新它,json 适配器也可以做到这一点

[MAXJsonAdapter refreshObject: object delegate: nil fromDictionary: dict]

这些方法将读取 User 模型上的所有属性名并将其与字典中的值相匹配。所有的用户属性都将被适当填充,从而使 initWithJsonDict: 方法变得冗余,可以被删除。我们也可以轻松地使用相同的信息更新模型。

如果您有模型对象,并想从它创建一个字典以发送回服务器或出于其他原因,可以使用以下方法

NSDictionary *objectDict = [MAXJsonAdapter MAXJADictFromObject: object delegate: nil];

当模型中的属性名与字典中的属性名不同,或者位于不同的字段中时,我们可以使用属性映射器将它们的数据从不同的字段中加载,或者将它们导出到不同的字段中的 json。有关属性映射的更多信息,请参阅下面的映射值章节。您可以通过遵循 MAXJsonAdapterDelegate 协议并传递一个委托参数到 MAXJsonAdapter 中来映射这些值。

我们在字典中经常从 APIs 收到不同格式的数据,例如日期,它们需要从 NSString 类型转换为 NSDate 类型,为此,您可以通过遵循 MAXJsonAdapterDelegate 协议并传递一个委托参数到 MAXJsonAdapter 中来使用值转换器。有关值转换器的更多信息,请参阅下面的章节。

映射值

让我们重新使用之前实现的用户类接口。但这次从服务器发送的 JSON 格式改变了,属性名也不再匹配。在这种情况下,我们使类遵循 MAXJsonAdapterDelegate 协议,并实现 MAJAPropertiesToMapObjectCreation 和 MAXJApropertiesToMapDictionaryCreation 以便在从对象创建 json 字典或从 json 字典创建对象时可以不同地映射属性。

@interface User : NSObject <MAXJsonAdapterDelegate>

@property (nonatomic, strong) NSString *email;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *middleName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *phoneNumber;

@property (nonatomic, strong) NSArray <NSString *> *usernames;

@end

新的用户 json 数据格式

{
    "personInfo" : {
        "firstName" : "Bruce",
        "middleName" : null,
        "lastName" : "Wayne"
    },
    "phoneNumber" : "7728282737"
    "userEmail" : "[email protected]",
    "aliases" : ["KillTheJoker", "RobinIsMyPet101", "TheRiddlerRiddlesTheRiddle"]
}

现在名称属性嵌入了一个名为 person info 的字段中,并且电子邮件和用户名已重命名为 userEmail 和 aliases。为了确保正确序列化属性,我们需要在 User 文件的实现中添加新的属性映射器。

@implementation User


-(NSArray <MAXJAPropertyMap *> *)MAXJAPropertiesToMapObjectCreation {
return @[[MAXJAPropertyMap MAXJAMapWithKey: @"firstName" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"personInfo" propertyMap: [MAXJAMapWithKey: @"firstName" propertyMap: nil]]],
[MAXJAPropertyMap MAXJAMapWithKey: @"middleName" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"personInfo" propertyMap: [MAXJAMapWithKey: @"middleName" propertyMap: nil]]],
[MAXJAPropertyMap MAXJAMapWithKey: @"lastName" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"personInfo" propertyMap: [MAXJAMapWithKey: @"lastName" propertyMap: nil]]],
[MAXJAPropertyMap MAXJAMapWithKey: @"email" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"userEmail" propertyMap: nil]],
[MAXJAPropertyMap MAXJAMapWithKey: @"usernames" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"aliases" propertyMap: nil]]
];
}

@end

在 MAXJAPropertyMap 对象的数组中,我们首先分配我们想要映射值的键的名称,例如 firstName,然后宣布我们想要在 json 中钻取的下一个键,即 personInfo,然后宣布获取值的下一个键是 firstName。当 MAXJsonAdapter 需要钻取 json 字段以查找某些键的值时,它将检查是否已声明属性映射器。对于电子邮件属性,我们只是声明一个新的映射到 userEmail 上,因为名称已更改,并且只映射名称,无需映射更深层次的 json。

为了使此工作,我们只需以下方式将包含委托的实例对象的实例传递给 MAXJsonAdapter

User *user = [MAXJsonAdapter MAXJAObjectOfClass: [User class] delegate: [[User alloc] init] fromDictionary: jsonDict];

您可以使任何 NSObject 遵循 MAXJsonAdapterDelegate 以映射值、转换它们并执行许多其他运行时任务。为了方便,我们将其放在了用户类中。

如果您需要创建一个 NSDictionary,并将其作为 json 发送到服务器,则 json 适配器还支持在从对象创建 NSDictionary 时映射值。我们再次使用 MAXJsonAdapterDelegate 协议并实现方法 MAXJAPropertiesToMapDictionaryCreation:

@implementation User

-(NSArray <MAXJAPropertyMap *> *)MAXJAPropertiesToMapDictionaryCreation {
return @[[MAXJAPropertyMap MAXJAMapWithKey: @"firstName" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"personInfo" propertyMap: [MAXJAMapWithKey: @"firstName" propertyMap: nil]]],
[MAXJAPropertyMap MAXJAMapWithKey: @"middleName" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"personInfo" propertyMap: [MAXJAMapWithKey: @"middleName" propertyMap: nil]]],
[MAXJAPropertyMap MAXJAMapWithKey: @"lastName" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"personInfo" propertyMap: [MAXJAMapWithKey: @"lastName" propertyMap: nil]]],
[MAXJAPropertyMap MAXJAMapWithKey: @"email" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"userEmail" propertyMap: nil]],
[MAXJAPropertyMap MAXJAMapWithKey: @"usernames" propertyMap: [MAXJAPropertyMap MAXJAMapWithKey: @"aliases" propertyMap: nil]]
];
}

使用此映射,我们将创建与从服务器接收的完全相同的 NSDictionary。我们可以使用以下方法从用户对象创建 NSDictionary

NSDictionary *userDict = [MAXJsonAdapter MAXJADictFromObject: user delegate: user];

这假设 User 对象遵循 MAXJsonAdapterDelegate 协议。这将创建如下格式的 json

新的用户 json 数据格式

{
"personInfo" : {
"firstName" : "Bruce",
"middleName" : null,
"lastName" : "Wayne"
},
"phoneNumber" : "7728282737"
"userEmail" : "[email protected]",
"aliases" : ["KillTheJoker", "RobinIsMyPet101", "TheRiddlerRiddlesTheRiddle"]
}

忽略或指定值

如果您想在从对象创建对象或字典时忽略某个属性,可以使用MAXJsonAdapterDelegate协议中的MAXJAPropertiesToIgnoreObjectCreation:方法或MAXJAPropertiesToIgnoreDictionaryCreation:方法来列出要忽略的属性。

以下是在User模型上的一个示例实现,该实现在创建其json时将忽略用户模型上的email属性。

@implementation User

-(NSArray <NSString *> *)MAXJAPropertiesToIgnoreDictionaryCreation {
return @[@"email"];
}

@end

通过向json适配器代理中适当的方法提供一个字符串数组,包含要忽略的属性名,您可以忽略任何想要的属性。

有时候,模型上有许多属性,您只想从中选择几个来创建字典。例如,您想更新用名,但后端只支持更新用户的第一、中间和姓氏。在这些情况下,声明您想要在从该对象创建对象或字典时使用的属性可能更有用。您可以通过实现MAXJsonAdapterDelegate协议中的MAXJAPropertiesForDictionaryCreation:或MAXJAPropertiesForObjectCreation:方法来实现。

以下是在User模型上的一个示例实现,其中我们只想创建包含用户名字的字典,不包含其他属性。

@implementation User

-(NSArray <NSString *> *)MAXJAPropertiesForDictionaryCreation {
return @[@"firstName", @"lastName", @"middleName"];
}

@end

当一个用户实例作为代理传递以创建json字典时,json将如下所示

{
    "firstName" : "Bruce",
    "middleName" : null,
    "lastName" : "Wayne"
}

通过这种方法,您可以显着缩短需要声明的需要忽略的属性列表的数量。

请注意,如果您同时实现了要忽略的属性和声明的属性,则声明的属性将具有优先权,不会同时使用,只能使用其中一个。

值转换器

当创建模型对象时,通常需要转换一个属性,例如将String转换为日期,这是一个非常常见的用例。为了转换一个值,您首先需要使用MAXJAPropertyValueTransformers:协议方法声明您想使用的转换器。您还需要通过其适当的方法子类化MAXJAValueTransformer以转换该值。

MAXJAValueTransformer子类的示例实现,它将String转换为日期,然后在从对象创建NSDictionary时将日期转换回String。

static NSDateFormatter *_formatter = nil;

@implementation DateValueTransformer

+(NSDateFormatter *)formatter {

    if(_formatter == nil) {
        _formatter = [[NSDateFormatter alloc] init];
        [_formatter setDateFormat:@"yyyy-MM-dd"];
    }
    
    return _formatter;
}

-(id)MAXJAObjectCreationFormat:(id)value {

    if(value == nil) {
    return nil;
    }
    
    if([value isKindOfClass: [NSString class]]) {
        NSDate *date = [_formatter dateFromString: (NSString *)value];
        return date;
    }
    
    return nil;

}

-(id)MAXJsonFormat:(id)value {

    if(value == nil) {
    return nil;
    }
    
    if([value isKindOfClass: [NSDate class]]) {
        NSString *dateString = [_formatter stringFromDate: (NSDate*)value];
        return dateString;
    }
    
    return nil;
}

@end

有两个方法需要实现:创建对象时要转换的值MAXJAObjectCreationFormat:和创建字典时要转换的值MAXJsonFormat:方法。

一旦实现这些方法,您需要在MAXJsonAdapterDelegate的MAXJAPropertyValueTransformers方法中将它们添加到中,在方法中,您声明要将值转换器用于哪些属性。在下面的模型中,我们有两个需要与值转换器相关联的日期属性。

@interface User : NSObject

@property (nonatomic, strong) NSString *email;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *middleName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *phoneNumber;

@property (nonatomic, strong) NSArray <NSString *> *usernames;

@property (nonatomic, strong) NSDate *createdAt;
@property (nonatomic, strong) NSDate *updatedAt;

@end
@implementation User

-(NSArray <MAXJAValueTransformer *> *)MAXJAPropertyValueTransformers {
return @[[DateValueTransformer MAXJAValueTransformerWithPropertyKeys: @[@"createdAt", @"updatedAt"]];
}

@end

在上面的实现中,createdAt和updatedAt属性将使用我们上面实现的类将值从字符串转换为日期,然后再从日期转换回字符串。如果属性有映射,只需在映射方法中声明它们,JSON适配器将负责其余工作。

子类化

通常,你会在类中将自己的类作为属性。使用MAXJsonAdapter序列化和反序列化子类化的属性非常容易。只需在MAXJsonAdapterDelegate约定对象中实现MAXJASubclassedProperties:方法。您还可以添加一个实例作为子类化属性的委托,以便可以使用适当的运行时转换。

添加子类化属性就像以下实现一样简单

@implementation User

-(NSArray <MAXJASublcassedProperty *> *)MAXJASubclassedProperties {
return @[[MAXJASubclassedProperty MAXJAPropertyKey: @"userInfo" class: [UserInfo class] delegate: nil];
}

@end

这就是添加子类化属性的全部内容。同样,与值转换器类似,子类化属性也支持使用委托中的属性映射方法进行属性映射。

要求

MAXJsonAdapter要求传递给它的模型对象是NSObject或符合键值编码的其他对象类型的子类。

与其他需要子类化的JSON适配器不同,此适配器不需要特定的子类化,因此可以轻松使用需要子类化的其他库。

安装

MAXJsonAdapter可通过CocoaPods获取。要安装,请将以下行添加到您的Podfile中

pod 'MAXJsonAdapter'

作者

roodoodey,Mathieu Grettir Skúlason。

许可证

MAXJsonAdapter可供MIT许可证使用。有关更多信息,请参阅LICENSE文件。