amap_map/ios/Classes/Util/AMapJsonUtils.m

276 lines
10 KiB
Mathematica
Raw Permalink Normal View History

2023-12-22 21:23:24 +08:00
//
// 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