AMapJsonUtils.m 11 KB

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