2014-03-03 3 views
2

다음 모델을 예제로 사용하면 JSONModel 내에서 다형성을 처리하는 가장 좋은 방법은 무엇입니까? GameModel이 {id:1, events:[{point:{x:1, y:1}, timestamp:...}]}JSONModel iOS 및 다형성

의 JSON 문자열로 시작되면

@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 

는 JSONModel는 GameEventModel를 사용하고 point 속성을 무시합니다.

{id:1, events:[{ type:"GameTouchEventModel", info:{ point:{x:1, y:1}, timestamp:... } }]}

이의 문제로 JSON을 받아 들일 수와 같은 type 재산 및 info 속성을 포함하는 일반적인 GameEventModel ...

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

그러므로 모델을 사용하는 것이 좋을 것이다 접근법은 코드를 읽는 것이 어렵고 컴파일러의 경고/오류도 없습니다.

JSONModel에서 다형성 모델을 사용할 방법이 없습니까?

답변

2

의 두 가지 사소한 변경으로이 문제를 해결했으며 JSONModel 파서에서 픽업 한 새로운 특수 JSON 속성 __subclass을 도입하고 값을 오브젝트 유형으로 사용합니다. __subclass은 예약 키워드 여야합니다 (따라서 어떤 모델도 속성 이름으로 __subclass을 사용할 수 없습니다).

수선

// ... 
-(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]; 
     // ... 
// ... 

JSONModel.m에 참고 : _subclass은 '존재하지 않는 JSON 모델 클래스를 에드 경우, 모델은 슈퍼 클래스로 폴백됩니다. json으로 문자열 {id:1, events:[ { __subclass:'GameTouchEventModel', timestamp:1, point: [0,0] } ] }

0

을 통과 할 때

이는 내가 BWJSONMatcher 매우 깔끔한 방법으로 그것을 처리 할 수 ​​있다고 생각 다음 모델

@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 

작동합니다. 다음과 같이

모델을 선언 :

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

T 그는 사용 방법에 대한 예제 BWJSONMatcher 다형성을 처리하는 데 here 찾을 수 있습니다.

0

TL; DR 도울 수 자신감 사용

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

}