amap_map/ios/Classes/Util/AMapJsonUtils.m

276 lines
10 KiB
Objective-C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// MAJsonUtils.m
// amap_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
+ (BOOL)isValidJsonValue:(id)value {
if([value isKindOfClass:NSString.class] ||
[value isKindOfClass:NSNumber.class] ||
value == [NSNull null]) {
return YES;
}
return NO;
}
+ (id)jsonValueFromObject:(id)obj {
if([self isValidJsonValue:obj]) {
return obj;
}
if(!obj) {
return [NSNull null];
}
//常用基本类型判断not exhausted
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;
}
+ (id)jsonObjectFromModel:(id)model {
id ret = [self jsonValueFromObject:model];
if(![NSJSONSerialization isValidJSONObject:ret]) {
return nil;
}
return ret;
}
+ (id)modelFromDict:(NSDictionary*)dict modelClass:(Class)modelClass {
if(![dict isKindOfClass:[NSDictionary class]]) {
NSLog(@"[AMap] the object must be of %@", [NSDictionary class]);
return nil;
}
if([modelClass isSubclassOfClass:[NSDictionary class]]) {
return [dict copy];
}
//获取clazz属性列表
NSArray *propertyArray = [self allPropertiesOfClass:modelClass];
NSMutableArray* missedProperties = [NSMutableArray array];
id ret = [[modelClass alloc] init];
//枚举clazz中的每个属性然后赋值
for (NSString *propertyName in propertyArray) {
NSString *keyName = propertyName;
id value = [dict objectForKey:keyName];
//'id'是关键字,服务端返回'id'字段属性名更改为'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];
//使用msgSend直接设置经纬度的属性
SEL sel = NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]);
((void (*)(id,SEL,CGPoint))objc_msgSend)(ret,sel,point);
continue;
}
}
//获取property类型后再处理
if(propertyClass) {
if([value isKindOfClass:propertyClass]) {
//array 需要特殊处理
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"]) {
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 { //end of if(propertyClaz) 如@"Ti" @"Tf"
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;
}
//返回array of propertyNames
+ (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