AMapJsonUtils.m
10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//
// MAJsonUtils.m
// amap_flutter_map
//
// Created by shaobin on 2019/2/13.
// Copyright © 2019 Amap.com. All rights reserved.
//
#import "AMapJsonUtils.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import <CoreLocation/CoreLocation.h>
#import "AMapConvertUtil.h"
@implementation AMapJsonUtils
/// 判断是否为合法的 JSON 值(字符串、数字或 NSNull)
+ (BOOL)isValidJsonValue:(id)value {
if([value isKindOfClass:NSString.class] ||
[value isKindOfClass:NSNumber.class] ||
value == [NSNull null]) {
return YES;
}
return NO;
}
/// 将任意对象递归转换为 JSON 可序列化对象
+ (id)jsonValueFromObject:(id)obj {
if([self isValidJsonValue:obj]) {
return obj;
}
if(!obj) {
return [NSNull null];
}
// 常用基本类型转字符串
if([obj isKindOfClass:NSDate.class] ||
[obj isKindOfClass:NSData.class] ||
[obj isKindOfClass:NSValue.class]) {
NSString *retStr = [NSString stringWithFormat:@"%@", obj];
return retStr;
}
// 数组递归处理
if([obj isKindOfClass:NSArray.class]) {
NSArray *oldArray = (NSArray*)obj;
NSMutableArray *retArray = [NSMutableArray arrayWithCapacity:[oldArray count]];
for(id item in oldArray) {
id jsonValue = [self jsonValueFromObject:item];
[retArray addObject:jsonValue];
}
return retArray;
}
// 字典递归处理
if([obj isKindOfClass:NSDictionary.class]) {
NSDictionary *oldDict = (NSDictionary *)obj;
NSMutableDictionary *retDict = [NSMutableDictionary dictionaryWithCapacity:[oldDict count]];
for(id key in [oldDict allKeys]) {
id item = [oldDict objectForKey:key];
id jsonValue = [self jsonValueFromObject:item];
if(jsonValue) {
[retDict setObject:jsonValue forKey:key];
}
}
return retDict;
}
// 自定义对象递归处理属性
NSArray *propertyArray = [self allPropertiesOfClass:[obj class]];
NSMutableDictionary *returnDict = [NSMutableDictionary dictionaryWithCapacity:propertyArray.count];
for(NSString *property in propertyArray) {
id value = [obj valueForKey:property];
if(value) {
id jsonValue = [self jsonValueFromObject:value];
NSString *mappedName = property;
if(jsonValue) {
[returnDict setObject:jsonValue forKey:mappedName];
}
}
}
return returnDict;
}
/// 将模型对象转换为 JSON 对象(NSDictionary/NSArray),不可序列化时返回 nil
+ (id)jsonObjectFromModel:(id)model {
id ret = [self jsonValueFromObject:model];
if(![NSJSONSerialization isValidJSONObject:ret]) {
return nil;
}
return ret;
}
/// 字典转模型对象,支持嵌套、数组、基本类型、UIColor、经纬度等
+ (id)modelFromDict:(NSDictionary*)dict modelClass:(Class)modelClass {
if(![dict isKindOfClass:[NSDictionary class]]) {
NSLog(@"[AMap] the object must be of %@", [NSDictionary class]);
return nil;
}
// 如果目标类型本身是 NSDictionary,直接返回副本
if([modelClass isSubclassOfClass:[NSDictionary class]]) {
return [dict copy];
}
// 获取属性列表
NSArray *propertyArray = [self allPropertiesOfClass:modelClass];
NSMutableArray* missedProperties = [NSMutableArray array];
id ret = [[modelClass alloc] init];
// 遍历属性并赋值
for (NSString *propertyName in propertyArray) {
NSString *keyName = propertyName;
id value = [dict objectForKey:keyName];
// 兼容 id 字段
if(!value && [propertyName isEqualToString:@"id_"]) {
value = [dict objectForKey:@"id"];
}
if(!value) {
[missedProperties addObject:propertyName];
continue;
}
if(value == [NSNull null]) {
continue;
}
// 获取属性类型
Class propertyClass = nil;
objc_property_t property = class_getProperty(modelClass, [propertyName UTF8String]);
NSString *propertyAttributes = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
NSArray *splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@","];
if(splitPropertyAttributes.count > 0) {
NSString *encodeType = splitPropertyAttributes[0];
if([encodeType hasPrefix:@"T@"]) {
NSArray *splitEncodeType = [encodeType componentsSeparatedByString:@"\""];
NSString *className = nil;
if(splitEncodeType.count > 1) {
className = splitEncodeType[1];
}
if(className) {
propertyClass = NSClassFromString(className);
}
} else if ([encodeType isEqualToString:@"T{CLLocationCoordinate2D=dd}"]) {// 经纬度
// 解析经纬度
CLLocationCoordinate2D coordinate = [self coordinateFromModel:value];
// 使用 msgSend 直接设置经纬度属性
SEL sel = NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]);
((void (*)(id,SEL,CLLocationCoordinate2D))objc_msgSend)(ret,sel,coordinate);
continue;
} else if ([encodeType isEqualToString:@"T{CGPoint=dd}"]) {// CGPoint
CGPoint point = [AMapConvertUtil pointFromArray:value];
SEL sel = NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]);
((void (*)(id,SEL,CGPoint))objc_msgSend)(ret,sel,point);
continue;
}
}
// 处理属性类型
if(propertyClass) {
if([value isKindOfClass:propertyClass]) {
// 数组类型特殊处理
if([propertyClass isSubclassOfClass:NSArray.class]) {
NSString *elementClassSel = [NSString stringWithFormat:@"%@ElementClass", propertyName];
SEL selector = NSSelectorFromString(elementClassSel);
if([[ret class] respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
Class elementCls = [[ret class] performSelector:selector];
#pragma clang diagnostic pop
NSArray *arr = (NSArray *)value;
NSMutableArray *mutArr = [NSMutableArray arrayWithCapacity:arr.count];
for(id item in arr) {
id newItem = [self modelFromDict:item modelClass:elementCls];
if(newItem) {
[mutArr addObject:newItem];
} else {
[mutArr addObject:item];
}
}
[ret setValue:mutArr forKey:propertyName];
} else {
[ret setValue:value forKey:propertyName];
}
} else {
[ret setValue:value forKey:propertyName];
}
} else if([value isKindOfClass:NSDictionary.class]){
NSDictionary *tempDic = value;
id model = [self modelFromDict:tempDic modelClass:propertyClass];
[ret setValue:model forKey:propertyName];
} else if ([value isKindOfClass:[NSNumber class]] && [NSStringFromClass(propertyClass) isEqualToString:@"UIColor"]) {
// NSNumber 转 UIColor
UIColor *color = [AMapConvertUtil colorFromNumber:value];
[ret setValue:color forKey:propertyName];
} else {
[ret setValue:value forKey:propertyName];
#ifdef DEBUG
Class valueClaz = [value class];
NSLog(@"\U0001F913\U0001F913 Warning1: property '%@' of %@ is %@, %@ is received", propertyName, modelClass, propertyClass, valueClaz);
#endif
}
} else { // 基本类型
if([self isValidJsonValue:value]){
[ret setValue:value forKey:propertyName];
} else {
#ifdef DEBUG
Class valueClaz = [value class];
NSLog(@"\U0001F913\U0001F913 Warning1: property '%@' of %@ is %@, %@ is received", propertyName, modelClass, propertyClass, valueClaz);
#endif
}
}
}
#ifdef DEBUG
if([missedProperties count] > 0) {
NSLog(@"\U0001F913\U0001F913 Warning2: %@ value missed: %@", modelClass, missedProperties);
}
#endif
// 支持模型自定义后处理钩子
NSString *postHookSel = [NSString stringWithFormat:@"postHookWith:"];
SEL sel = NSSelectorFromString(postHookSel);
if([ret respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[ret performSelector:sel withObject:dict];
#pragma clang diagnostic pop
}
return ret;
}
/// 获取类的所有属性名(包含父类)
+ (NSArray<NSString*> *)allPropertiesOfClass:(Class)cls {
Class clazz = cls;
NSMutableArray *mutArr = [[NSMutableArray alloc] init];
while(clazz != [NSObject class]) {
unsigned int count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (int i = 0; i < count ; i++) {
objc_property_t prop = properties[i];
NSString *propertyName = [NSString stringWithCString:property_getName(prop) encoding:NSUTF8StringEncoding];
[mutArr addObject:propertyName];
}
if(properties) {
free(properties);
}
clazz = class_getSuperclass(clazz);
}
return mutArr;
}
/// 从数据 model 中解析经纬度
+ (CLLocationCoordinate2D)coordinateFromModel:(id)model {
CLLocationCoordinate2D location = kCLLocationCoordinate2DInvalid;
if ([model isKindOfClass:[NSArray class]]) {
return [AMapConvertUtil coordinateFromArray:model];
} else if ([model isKindOfClass:[NSString class]]) {// 字符串格式(经度,纬度),需逆置
NSString *coordStr = model;
NSArray *array = [coordStr componentsSeparatedByString:@","];
array = [[array reverseObjectEnumerator] allObjects];//逆置
return [AMapConvertUtil coordinateFromArray:array];
} else if ([model isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = model;
NSNumber *latitudeNum = [dict objectForKey:@"latitude"];
NSNumber *longitudeNum = [dict objectForKey:@"longitude"];
if (latitudeNum && longitudeNum) {
location = CLLocationCoordinate2DMake([latitudeNum doubleValue], [longitudeNum doubleValue]);
} else {
NSLog(@"经纬度参数异常,解析为无效经纬度");
}
}
return location;
}
@end