許多現代編程語言都有 JSON 庫,支持編碼和解碼 json 到/從“普通舊物件” - 即主要只有資料屬性的類的實體(屬性可以是可以簡單地解碼/編碼的型別或其他普通的舊物件)。示例包括 Google 的 GSON、golangencoding/json等。
有沒有類似于Objective-C的東西?
我知道可以列舉 Objective-C 類的屬性,而且有人會使用該功能創建 JSON“bean 映射器”似乎是合理的,但谷歌搜索對我沒有產生任何結果,除了Apple 上的這篇博客文章Swift 網站展示了如何手動將 JSON 反序列化為“模型物件”,以及為什么他們認為自動執行此操作(干燥代碼)是一個壞主意。
uj5u.com熱心網友回復:
這是我的解決方案,它不是基于庫 - 因為我找不到任何庫 - 而是使用 Foundation 和 Objective-C 運行時方法 - 正如上面的評論中所討論的:
#import <objc/runtime.h>
NSArray<NSString*>* classPropertyList(id instance) {
NSMutableArray* propList = [NSMutableArray array];
unsigned int numProps = 0;
objc_property_t* props = class_copyPropertyList(object_getClass(instance), &numProps);
for (int i = 0; i < numProps; i )
[propList addObject:[NSString stringWithUTF8String:property_getName(props[i])]];
free(props);
return propList;
}
NSString* typeOfProperty(Class clazz, NSString* propertyName) {
objc_property_t prop = class_getProperty(clazz, [propertyName UTF8String]);
NSArray<NSString*>* propAttrs = [[NSString stringWithUTF8String:property_getAttributes(prop)] componentsSeparatedByString:@","];
if ([(propAttrs[0]) hasPrefix:@"T@\""])
return [propAttrs[0] componentsSeparatedByString:@"\""][1];
return nil;
}
@implementation JSONMarshallable
- (NSData*)toJSON {
return [self toJSON:self withNullValues:YES];
}
- (NSString*)toJSONString {
return [self toJSONString:self withNullValues:YES];
}
- (NSData*)toJSON:_ withNullValues:(bool)nullables {
NSError* error;
NSDictionary* dic = [self toDictionary:self withNullValues:nullables];
NSData* json = [NSJSONSerialization dataWithJSONObject:dic options:0 error:&error];
if (!json) {
NSLog(@"Error encoding DeviceConfigurationRequest: %@", error);
return nil;
}
return json;
}
- (NSString*) toJSONString:_ withNullValues:(bool)nullables {
NSData* json = [self toJSON:self withNullValues:nullables];
return [[NSString alloc] initWithBytes:[json bytes] length:[json length] encoding:NSUTF8StringEncoding];
}
- (NSDictionary*)toDictionary:_ withNullValues:(bool)nullables {
NSMutableDictionary* dic = [NSMutableDictionary new];
for (id propName in classPropertyList(self)) {
id val = [self valueForKey:propName];
if (!nullables && (val == nil || val == NSNull.null))
continue;
if ([val respondsToSelector:@selector(toDictionary:withNullValues:)])
val = [val toDictionary:val withNullValues:nullables];
[dic setObject:(val == nil ? NSNull.null : val) forKey:propName];
}
return dic;
}
- (instancetype)initWithJSONString:(NSString*)json {
return [self initWithJSON:[json dataUsingEncoding:NSUTF8StringEncoding]];
}
- (instancetype)initWithJSON:(NSData*)json {
NSError* error;
if (json == nil)
return nil;
NSDictionary* dataValues = [NSJSONSerialization JSONObjectWithData:json options:0 error:&error];
if (!dataValues) {
NSLog(@"Error parsing invalid JSON for %@: %@", NSStringFromClass(object_getClass(self)), error);
return nil;
}
return [self initWithDictionary:dataValues];
}
- (instancetype)initWithDictionary:(NSDictionary*)dataValues {
if (dataValues == nil)
return nil;
if (self = [super init])
for (id key in dataValues) {
id val = [dataValues objectForKey:key];
if (![self respondsToSelector:NSSelectorFromString(key)])
continue;
NSString* typeName = typeOfProperty([self class], key);
if ([val isKindOfClass:[NSNull class]]) { // translate NSNull values to something useful, if we can
if (typeName == nil)
continue; // don't try to set nil to non-pointer fields
val = nil;
} else if ([val isKindOfClass:[NSDictionary class]] && typeName != nil)
val = [[NSClassFromString(typeName) alloc] initWithDictionary:val];
[self setValue:val forKey:key];
}
return self;
}
@end
然后很容易通過繼承來創建自定義模型物件JSONMarshallable,如下所示:
model.h:
#import "JSONMarshallable.h"
@interface MyModel : JSONMarshallable
@property NSString* stringValue;
@property NSNumber* numericValue;
@property bool boolValue;
@end
model.m:
@implementation MyModel
@end
SomeThingElse.m:
// ...
NSData* someJson;
MyModel* obj = [[MyModel alloc] initWithJSON:someJson];
NSString* jsonObj = [obj toJSONString:nil withNullValues:NO];
歡迎批評!(我不太擅長 Objective C,可能做了很多失禮??)
問題:
- 我可以處理可以為空的數字
NSNumber*(盡管 C 原語對不可為空的數字作業得很好),但我不知道如何表示可以為空的布林值 - 即一個可選的欄位,在使用withNullValues:NO. 發送沒有屬性的欄位(例如,我使用的服務器以蛇形和下劃線形式發送值以使其易于決議)會引發例外。(通過使用respondsToSelector:andsetValue:代替解決setValuesForKeysWithDictionary:)。嘗試將(通過檢查屬性型別和解決nil值設定為原始型別欄位會導致例外。NSNull)。根本不適用于嵌套物件 - 即具有也是自定義模型物件的屬性的自定義模型物件。(通過檢查屬性型別和遞回編碼/解碼來解決)。- 可能不能很好地處理陣列 - 我的軟體中還沒有需要這些,所以我沒有實作適當的支持(盡管我驗證了編碼簡單字串陣列的效果很好)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/477647.html
