2

使用以下模型作为示例,在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 使用 JSON 字符串启动时{id:1, events:[{point:{x:1, y:1}, timestamp:...}]}

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

GameEventModel使用包含type属性和属性的泛型会更好,info例如...

@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 中使用多态模型?

4

3 回答 3

2

我们通过对 的 2 个小改动解决了这个问题JSONModel.m,引入了一个新的特殊 JSON 属性__subclass,该属性被解析器拾取JSONModel并使用该值作为对象类型。__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'ed JSON 模型类不存在,则模型将回退到超类。

这将适用于以下模型

@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

当传递 JSON 字符串时{id:1, events:[ { __subclass:'GameTouchEventModel', timestamp:1, point: [0,0] } ] }

于 2014-03-04T01:30:37.823 回答
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的实现中,实现这个函数:

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

    return nil;
}

然后您可以在一行中从 json 字符串中获取您自己的数据实例:

GameModel *gameModel = [GameModel fromJSONString:jsonString];

可以在这里找到有关如何使用BWJSONMatcher处理多态性的示例。

于 2015-11-10T07:31:03.580 回答
0

TL;博士

使用 Swagger 可能会有所帮助,请参阅github中的此示例。

关于招摇

公认的解决方案是一种方法,但我想提供另一种方法。如果您使用Swagger生成模型并利用“allOf/discriminator”特性来实现继承,则生成的 Objective-C 类将包含与公认解决方案提供的代码相似的代码。

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];

}
于 2016-05-15T02:35:39.797 回答