KZPropertyMapper 2.9

KZPropertyMapper 2.9

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布上次发布2017年12月

Krzysztof ZablockiKrzysztof Zabłocki 维护。



  • 作者:
  • Krzysztof Zablocki

CI Status
codecov
Version
License
Platform

在 iOS 应用中停止重复数据解析代码。

数据解析是我们需要在应用中执行的最常见任务之一,但仍有大多数人在手动进行解析,重复编写相同的代码来映射每个类。

通常的解析需要以下步骤

  • 确保将 NSNull 转换为 nil 以避免崩溃
  • 优雅地处理可选参数
  • 进行类型转换
  • 数据格式验证

为何使用属性映射器?

有一些库可以帮助完成这项工作,例如 Mantle、RESTKit 以及更多……但我想要一个自包含的、易于更改/删除且不需要太多代码的库。

我在对 Foldify 进行开发时创建了 Property Mapper,这是一个简单的自包含解决方案,允许您指定数据之间的映射,这些数据是你接收到的,以及在您的应用中的数据表示……还有一些附加功能,如 类型包装验证

我不喜欢传递 JSON,因此我在自带的 NSDictionary/NSArray 对象上编写解析。
如果您以 JSON 的形式得到数据,只需编写一个简单的分类,将 JSON 转换为原生对象即可使用 NSJSONSerialization。

示例用法

假设您有一个这样的对象

@interface TestObject : NSObject
@property(nonatomic, strong) NSURL *contentURL;
@property(nonatomic, strong) NSURL *videoURL;
@property(nonatomic, strong) NSNumber *type;
@property(nonatomic, strong) NSString *title;
@property(nonatomic, strong) NSString *uniqueID;

@end

并且您从服务器以这种格式收到数据

@{
  @"videoURL" : @"http://test.com/video.mp4",
	@"name" : @"Some Cool Video",
	@"videoType" : [NSNull null],
	@"sub_object" : @{
			@"title" : @616,
			@"arbitraryData" : @"data"
	}
}

这是您在解析代码中会编写的代码

[KZPropertyMapper mapValuesFrom:dictionary toInstance:self usingMapping:@{
   @"videoURL" : KZBox(URL, contentURL),
     @"name" : KZProperty(title),
     @"videoType" : KZProperty(type),
     @"sub_object" : @{
         @"title" : KZProperty(uniqueID)
         }

  }];

很明显它在做什么,但如果您不明白,它将把 videoURL 字符串转换为 contentURL NudeURL 对象,它还将从 sub_object 中获取标题并将其赋给 uniqueID。它还处理 NSNull。

高级用法

现在,让我们换个主意,决定我们的类型属性应该是 typedef 枚举,使用 KZPropertyMapper 来实现这一点非常简单,将类型映射更改为以下内容并添加以下方法

@"videoType" : KZCall(videoTypeFromString:, type),

//! implemented on instance you are parsing
- (id)videoTypeFromString:(NSString *)type
{
  if ([type isEqualToString:@"shortVideo"]) {
    return @(VideoTypeShort);
  }

  return @(VideoTypeLong);
}

如果您需要在使用选择器时传递属性名,只需在 KZCall 中传递一个两个参数的选择器。第二个参数将是作为 NSString 传递的属性名。

@"video" : KZCall(objectFromDictionary:forPropertyName:, type),

//! implemented on instance you are parsing
- (id)objectFromDictionary:(NSDictionary *)dictionary forPropertyName:(NSString *)propertyName
{
  Class objectClass = [self classForProperty:propertyName];
  id object = [objectClass new];
  [object updateFromDictionary:dictionary];
  return object;
}

完成。KVC 还应该负责将 NSNumber 转换为 int,如果您的属性使用基本类型。相同的方法也适用于子对象实例或您可以分配给属性的任何东西。

验证

您还可以在映射之前验证服务器数据

[KZPropertyMapper mapValuesFrom:dictionary toInstance:self usingMapping:@{
    @"videoURL" : KZBox(URL, contentURL).isRequired().min(10),
    @"name" : KZProperty(title).lengthRange(5, 12),
    @"videoType" : KZProperty(type),
    @"sub_object" : @{
      @"title" : KZProperty(uniqueID),
    },
  }];

验证器可以连接在一起,您可以指定任意多的验证器用于每个字段,验证在映射之前发生在源数据上。

如果验证失败,mapValues 将返回 NO 作为结果,并且您可以使用扩展方法获取验证错误列表。

任何验证错误都将阻止映射,因为数据可能已损坏,我们不希望得到部分更新的数据。

内置验证

字符串

  • 是否必需
  • 正则表达式匹配
  • 长度
  • 最小长度
  • 最大长度
  • 长度范围
  • 之一
  • 等于

数字

  • 最小值
  • 最大值
  • 范围

如果您需要更多,您可以在KZPropertyDescriptor上添加验证类别,查看示例代码,它非常简单。

引用数组项

如果您的数据是以数组而不是字典的形式提供给您,您也可以这样做。

sourceData = @{@"sub_object_array" : @[@"test", @123]}

@{@"sub_object_array" : @{@1 : KZProperty(uniqueID)}

这将从sub_object_array中获取第一个项并将其分配给uniqueID。这也适用于递归。

扩展装箱功能

您可以轻松地在整个应用程序中扩展装箱功能,只需在KZPropertyMapper中添加实现此类方法的功能即可

+ (id)boxValueAsType:(id)value
{
	//! your boxing
}

现在您可以在代码中任何地方使用 @type 映射。

变更日志

2.5.0

  • 添加了KZList,允许您将单个源属性映射到多个目标属性。
  • 解析时添加了类型检查,如果找到不匹配的类型,则忽略属性并生成警告。
  • 在处理意外的服务器响应时修复了几个小错误。
@{
  	@"videoURL" : KZList(
				KZBoxT(object, URL, contentURL),
				KZPropertyT(object, uniqueID),
				KZCallT(object, passthroughMethod:, type)
			)
}

Marek Cirkos 贡献

2.0

  • 新属性语法(旧语法仍然有效)允许您在拼写属性名称错误时出现编译错误。
  • 引入了验证器概念,可链式使用。

这些更改的实施由 The App Business 赞助。

安装

  • 使用CocoaPods:将以下行添加到您的 Podfile

    pod "KZPropertyMapper"

  • 使用Carthage:将其添加到您的 Cartfile

    github "krzysztofzablocki/KZPropertyMapper" "master"

  • 或者直接将 KZPropertyMapper/ 文件夹添加到您的项目中,确保启用这些文件上的ARC。

最后要注意的是

单元测试应作为文档。默认的装箱类型包括 @url@Date

关注我的推特