JSONModel iOS和多态性

使用以下模型作为示例,在JSONModel中处理多态的最佳实践是什么?

@interface GameModel : JSONModel @property (nonatomic, assign) long id; @property (nonatomic, assign) NSArray<GameEventModel> *events; /* ... */ @end @interface GameEventModel : JSONModel @property (nonatomic, assign) long long timestamp; /* ... */ @end @interface GameTouchEventModel : GameEventModel @property (nonatomic, assign) CGPoint point; /* ... */ @end 

当GameModel以{id:1, events:[{point:{x:1, y:1}, timestamp:...}]}的JSONstring启动时,

JSONModel将使用GameEventModel并忽略point属性。

使用包含type属性和info属性的通用GameEventModel会更好吗?比如…

 @interface GameTouchEventModel : GameEventModel @property (nonatomic, strong) NSString *type; @property (nonatomic, strong) NSDictionary *info; @end 

因此,模型可以接受JSON为{id:1, events:[{ type:"GameTouchEventModel", info:{ point:{x:1, y:1}, timestamp:... } }]}

这种方法的问题很难读取代码,并且没有编译器警告/错误等等。

有没有办法在JSONModel中使用多态模型?

我们通过对JSONModel.m 2个小改动解决了这个问题,引入了由JSONModelparsing器拾取的新的特殊JSON属性__subclass ,并使用该值作为对象types。 __subclass必须是一个保留关键字(因此没有模型可以使用__subclass作为属性名称)。

JSONModel.m

 // ... -(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err { // ... if ([self __isJSONModelSubClass:property.type]) { //initialize the property's model, store it JSONModelError* initErr = nil; -- id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr]; ++ id value; ++ if([jsonValue valueForKey:@"subclass"] != NULL) ++ { ++ Class jsonSubclass = NSClassFromString([d valueForKey:@"subclass"]); ++ if(jsonSubclass) ++ obj = [[jsonSubclass alloc] initWithDictionary:d error:&initErr]; ++ } ++ else ++ value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr]; //... //... +(NSMutableArray*)arrayOfModelsFromDictionaries:(NSArray*)array error:(NSError**)err { // ... for (NSDictionary* d in array) { JSONModelError* initErr = nil; -- id obj = [[self alloc] initWithDictionary:d error:&initErr]; ++ id obj; ++ if([d valueForKey:@"subclass"] != NULL) ++ { ++ Class jsonSubclass = NSClassFromString([d valueForKey:@"subclass"]); ++ if(jsonSubclass) ++ obj = [[jsonSubclass alloc] initWithDictionary:d error:&initErr]; ++ } ++ else ++ obj = [[self alloc] initWithDictionary:d error:&initErr]; // ... // ... 

注意:如果_subclass JSON模型类不存在,那么模型将回_subclass超类。

然后这将与以下模型一起工作

 @interface GameModel : JSONModel @property (nonatomic, assign) long id; @property (nonatomic, assign) NSArray<GameEventModel> *events; @end @protocol GameEventModel @end @interface GameEventModel : JSONModel @property (nonatomic, assign) long long timestamp; @end @interface GameTouchEventModel : GameEventModel @property (nonatomic, strong) NSArray *point; @end 

当传递JSONstring{id:1, events:[ { __subclass:'GameTouchEventModel', timestamp:1, point: [0,0] } ] }

我认为BWJSONMatcher能够以非常整洁的方式处理它。

声明你的模型如下:

 @interface GameModel : NSObject<BWJSONValueObject> @property (nonatomic, assign) long id; @property (nonatomic, strong) NSArray *events; @end @interface GameEventModel : NSObject @property (nonatomic, assign) long long timestamp; @end @interface GameTouchEventModel : GameEventModel @property (nonatomic, strong) NSDictionary *point; @end 

GameModel的实现中,实现这个function:

 - (Class)typeInProperty:(NSString *)property { if ([property isEqualToString:@"events"]) { return [GameEventModel class]; } return nil; } 

然后你可以在一行中从jsonstring中获得你自己的数据实例:

 GameModel *gameModel = [GameModel fromJSONString:jsonString]; 

关于如何使用BWJSONMatcher来处理多态性的例子可以在这里find。

TL; DR

使用Swagger可以提供帮助,请参阅github中的示例。

关于Swagger

接受的解决scheme是一种方式,但我想提供一个替代scheme。 如果使用Swagger生成模型并利用“allOf / discriminator”特性来实现inheritance,则生成的Objective-C类将包含与接受的解决scheme提供的类似的代码。

YAML

 definitions: Point: type: object properties: x: type: number y: type: number GameEventModel: type: object discriminator: gameEventModelType GameTouchEventModel: type: object description: GameTouchEventModel allOf: - $ref: '#/definitions/GameEventModel' - type: object properties: gameEventModelType: type: string point: $ref: '#/definitions/Point' GameFooEventModel: type: object description: GameTouchEventModel allOf: - $ref: '#/definitions/GameEventModel' - type: object properties: gameEventModelType: type: string name: type: string GameModel: type: object properties: id: type: integer format: int64 events: type: array items: $ref: '#/definitions/GameEventModel' 

生成的代码片段

 /** Maps "discriminator" value to the sub-class name, so that inheritance is supported. */ - (id)initWithDictionary:(NSDictionary *)dict error:(NSError *__autoreleasing *)err { NSString * discriminatedClassName = [dict valueForKey:@"gameEventModelType"]; if(discriminatedClassName == nil ){ return [super initWithDictionary:dict error:err]; } Class class = NSClassFromString([@"SWG" stringByAppendingString:discriminatedClassName]); if([self class ] == class) { return [super initWithDictionary:dict error:err]; } return [[class alloc] initWithDictionary:dict error: err]; }