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を含むジェネリックを使用する方がよいでしょうか...typeinfo

@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

パーサーによって取得され、値をオブジェクト タイプとして使用JSONModel.mする新しい特別な JSON プロパティを導入することで、に 2 つの小さな変更を加えることでこれを解決しました。は予約済みのキーワードである必要があります (したがって、モデルはプロパティ名として使用できません)。__subclassJSONModel__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

TL;DR

Swagger を使用すると役立つ場合があります。githubのこの例を参照してください。

Swaggerについて

受け入れられた解決策は 1 つの方法ですが、別の方法を提供したいと思います。Swaggerを使用してモデルを生成し、"allOf/discriminator" 機能を利用して継承を実装すると、生成された Objective-C クラスには、受け入れられたソリューションによって提供されるものと同様のコードが含まれます。

ヤムル

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 に答える
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;
}

そして、1 行で json 文字列から独自のデータ インスタンスを取得できます。

GameModel *gameModel = [GameModel fromJSONString:jsonString];

BWJSONMatcherを使用してポリモーフィズムを処理する方法の例は、ここにあります。

于 2015-11-10T07:31:03.580 に答える