Skip to content

Key mapper class#546

Open
billinghamj wants to merge 2 commits intomasterfrom
key-mapper-class
Open

Key mapper class#546
billinghamj wants to merge 2 commits intomasterfrom
key-mapper-class

Conversation

@billinghamj
Copy link
Copy Markdown
Member

No description provided.

@0wnrepo
Copy link
Copy Markdown

0wnrepo commented Sep 7, 2017

Looks awesome, i'm also going to use this.

@heistings
Copy link
Copy Markdown
Contributor

heistings commented Jan 29, 2018

I'm sorry, I don't understand.

Do this PR solve the problem below?

@interface BaseModel : NSObject
@property (nonatomic) NSString *property1; // There are more properties

@interface BaseModelForApi1 : BaseModel 
@end
@interface BaseModelForApi2 : BaseModel 
@end

@implementation BaseModelForApi1
+ (JSONKeyMapper)keyMapper
{
  return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"property1":@"api_1_field"}];
}
@end

@implementation BaseModelForApi2 
+ (JSONKeyMapper)keyMapper
{
  return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"property1":@"api_2_field"}];
}
@end

The key point is to map one property to more than one different keys ?

@heistings
Copy link
Copy Markdown
Contributor

heistings commented Jan 29, 2018

I think - (NSString *)convertValue:(NSString *)value forClass:(Class)cls; is not a good idea.

Multiple KeyMappers may be the best solution to #545 and #612, not Single KeyMapper play as multiple roles, taking Expansibility and Dependency Inversion into consideration.

The best situation should be only one JSONModel Class with multiple KeyMappers, not a JSONModel Cluster.

@heistings
Copy link
Copy Markdown
Contributor

heistings commented Jan 29, 2018

@billinghamj

I'm sorry for my poor comments above. I think you are right: One model with multiple KeyMappers will meet problem with nested models.

But I still don't understand. How does the method work for this issue ?

- (NSString *)convertValue:(NSString *)value forClass:(Class)cls;

How can you make your decision with the case blow:

api1:

{
	"orderId": 104,
	"totalPrice": 13.45,
	"product": {
		"id": 123,
		"name": "Product name",
		"price": 12.95
	}
}

api 2:

{
	"orderId": 104,
	"totalPrice": 13.45,
	"product": {
		"product_id": 123,
		"name": "Product name",
		"price": 12.95
	}
}
@interface ProductModel : JSONModel
@property (nonatomic) NSInteger id;
@property (nonatomic) NSString *name;
@property (nonatomic) float price;
@end
  
@interface ProductModelApi1 : ProductModel
@end
  
@interface ProductModelApi2 : ProductModel
@end

@interface OrderModel : JSONModel
@property (nonatomic) NSInteger orderId;
@property (nonatomic) float totalPrice;
@property (nonatomic) ProductModel *product;
@end

@billinghamj
Copy link
Copy Markdown
Member Author

This enables the issue to be resolved, to some degree, by enabling the keymapper to act differently based on the context of the class.

I agree it isn't a great solution, but to be honest I'm not sure what else we can do here. Do you have any other suggestions?

@heistings
Copy link
Copy Markdown
Contributor

@billinghamj Glad with your reply :)

I'm sorry I can not help a lot currently, I'm still thinking about it.

The word "context" you have mentioned above is quite close. The hardest part of solution is delivering the "context" through out the entire conversion.

@interface ProductModel : JSONModel
@property (nonatomic) NSInteger id;
@property (nonatomic) NSString *name;
@property (nonatomic) float price;
@end
  
@interface ProductModelApi1 : ProductModel
@end
  
@interface ProductModelApi2 : ProductModel
@end

@interface OrderModel : JSONModel
@property (nonatomic) NSInteger orderId;
@property (nonatomic) float totalPrice;
@property (nonatomic) ProductModel *product;
@end

When we call [OrderModel toDictionary], the "context" will be lost in [ProductModel toDictionary] stage.

I'm poor in Android, but I've heard that there is a "context" technology, which can be access through out some entire process. I think the "context" design could be helpful to our issue.

It's hard to say in words. The solution must be in higher dimensionality, not under. And If the "context" can be access through out, multithreading must be taken into consideration.

@heistings
Copy link
Copy Markdown
Contributor

heistings commented Jan 30, 2018

Class cluster should not be invited, since it will lead things complexing. When invited, there must be some code to copy ProductModelApi2 from ProductModelApi1. Thus there will be multi set of models. AClassApi1 AClassApi2 BClassApi1 BClassApi2 ... XClassApi1 XClassApi2.

I'd like to introduce some code, which could be inspired :

// Default Context

OrderModel *orderModel;

NSDictionary *dict = [orderModel toDictionary];

// Default Context End

// Context Api2

[JSONModelContext startNewContextWithIdentifier:@"Api2"];

NSDictionary *dict = [orderModel toDictionary];

[JSONModelContext endContext];

// Context Api2 End

There is only one set of model classes, and one key mapper for each. But we can enable key mapper to behave differently when it is under custom context than the default context.

@heistings
Copy link
Copy Markdown
Contributor

heistings commented Jan 30, 2018

Or we just bind the context with root instance, and the context will be passed on through out the entire serialization. Since global context will met problem with multi threading

// Default Context

OrderModel *orderModel;

NSDictionary *dict = [orderModel toDictionary];


// Context Api2 
JSONModelContext *api2Context [JSONModelContext contextWithIdentifier:@"Api2"];

NSDictionary *dict = [orderModel toDictionaryWithContext:api2Context];

@billinghamj
Copy link
Copy Markdown
Member Author

billinghamj commented Jan 30, 2018

I have previously considered a method like toDictionaryWithKeyMapper:, but that wouldn’t work for complex nested objects.

I like the idea of being able to say toDictionaryWithOptions: or something. It could possibly support other things in the future too. Perhaps that would be an NSDictionary. It would probably also need to allow initWithDictionary:options: too.

MyModel *obj = [[MyModel alloc] initWithData:data options:@{@"style":"snake_case"}];

[obj toDictionaryWithOptions:@{@"style":"camel_case"}];

The meaning of the options dictionary would be entirely up to the application developer - they’d just receive it back as-is in some methods.

This would replace the class input on the keymappers in this PR. (Though would anyone potentially still want the class, in addition?)

@heistings
Copy link
Copy Markdown
Contributor

@billinghamj

It seems great !

The options dictionary, which plays a role as context, will be delivered to every key mapper in the conversion chain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants