【疯狂造轮子-iOS】JSON转Model系列之二

本文转载请注明出处 —— polobymulberry-博客园


1. 前言


上一篇《【疯狂造轮子-iOS】JSON转Model系列之一》实现了一个简陋的JSON转Model的库,不过还存在很多问题。下面我会尝试一个个去解决。

2. 存在问题及解决思路


2.1 没有考虑JSON数据并不一定是NSDictionary类型

有时候JSON并不一定是NSDictionary类型,可能是一个字符串,也可能是NSData类型的数据。不过不管是哪种类型,统统先将其转化为NSData数据,然后使用+[NSJSONSerialization JSONObjectWithData:options:error:]来转化。所以我在initWithAttributes:上面又封装了一层。

  1. - (instancetype)initWithJSONData:(id)json
  2. {
  3. NSDictionary *dict = [self pjx_dictionaryWithJSON:json];
  4. return [self initWithAttributes:dict];
  5. }
  6.  
  7. /**
  8. * @brief 将NSString和NSData格式的json数据转化为NSDictionary类型
  9. */
  10. - (NSDictionary *)pjx_dictionaryWithJSON:(id)json
  11. {
  12. if (!json) {
  13. return nil;
  14. }
  15. // 若是NSDictionary类型,直接返回
  16. if ([json isKindOfClass:[NSDictionary class]]) {
  17. return json;
  18. }
  19.  
  20. NSDictionary *dict = nil;
  21. NSData *jsonData = nil;
  22.  
  23. if ([json isKindOfClass:[NSString class]]) {
  24. // 如果是NSString,就先转化为NSData
  25. jsonData = [(NSString*)json dataUsingEncoding:NSUTF8StringEncoding];
  26. } else if ([json isKindOfClass:[NSData class]]) {
  27. jsonData = json;
  28. }
  29.  
  30. if (jsonData && [jsonData isKindOfClass:[NSData class]]) {
  31. // 如果时NSData类型,使用NSJSONSerialization
  32. NSError *error = nil;
  33. dict = [NSJSONSerialization JSONObjectWithData:jsonData options: error:&error];
  34. if (error) {
  35. NSLog(@"pjx_dictionaryWithJSON error:%@", error);
  36. return nil;
  37. }
  38. if (![dict isKindOfClass:[NSDictionary class]]) {
  39. return nil;
  40. }
  41. }
  42.  
  43. return dict;
  44. }

为此,我在ViewController添加了两个sample。分别用来解析NSString类型的JSON数据和NSData类型的JSON数据。

  1. // NSString类型的JSON数据
  2. - (void)runSimpleSample2
  3. {
  4. NSString *userStr = @" \
  5. { \
  6. \"username\" : \"shuaige\", \
  7. \"password\" : \"123456\", \
  8. \"avatarImageURL\" : \"http://www.example.com/shuaige.png\" \
  9. }";
  10.  
  11. PJXUser *user = [[PJXUser alloc] initWithJSONData:userStr];
  12.  
  13. NSLog(@"runSimpleSample2\n");
  14. NSLog(@"----------------------------------------");
  15. NSLog(@"username:%@\n",user.username);
  16. NSLog(@"password:%@\n",user.password);
  17. NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
  18. }
  19.  
  20. // NSData类型的JSON数据
  21. - (void)runSimpleSample3
  22. {
  23. NSString *userInfoFilePath = [[NSBundle mainBundle] pathForResource:@"UserInfo" ofType:@"txt"];
  24. NSData *data = [NSData dataWithContentsOfFile:userInfoFilePath];
  25. PJXUser *user = [[PJXUser alloc] initWithJSONData:data];
  26.  
  27. NSLog(@"runSimpleSample3\n");
  28. NSLog(@"----------------------------------------");
  29. NSLog(@"username:%@\n",user.username);
  30. NSLog(@"password:%@\n",user.password);
  31. NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
  32. }

输出结果也是正确的:

2.2 没有考虑用户传入的JSON数据的key值和property的名称不一致

我第一反应是使用一个映射表。也就是说用户使用时需要自定义一套property和key的映射表。YYModel中使用了一个+ (NSDictionary *)modelCustomPropertyMapper函数,用户可以自定义该函数达到映射表的效果,而这个函数是放在一个protocol中的。我挺认同这种设计的,因为modelCustomPropertyMapper这种函数和Model是一种组合关系,可有可无(optional),所以设计成协议更合适。但是作者在设计protocol又说了一句:

  1. // There's no need to add '<YYModel>' to your class header.
  2. @protocol YYModel <NSObject>

什么意思呢,就是说你自定义一个NSObject子类(如YYBook)时,如果想实现自定义的property映射关系,只需要实现modelCustomPropertyMapper函数即可,而不需要写成@interface YYBook : NSObject <YYModel>。作者的意思是你遵不遵循YYModel这个protocol都没事,反正你只要在YYBook实现了modelCustomPropertyMapper即可。具体解释,大家请参考这个issue

这种设计我不是很赞同,我是有洁癖的人,要不然你就别定义YYModel这个protocol,说明文档里面着重说明一下就行。所以此处我还是选择判断NSObject的子类是否遵循protocol,也就是说只有遵循了这个protocol,才能自定义property映射关系。

首先我们看如何使用自定义propertyMapper。我先建立一个PJXUserPropertyMapper类,遵循了JSONProtocol协议,并实现了propertyMapper协议函数。

  1. // 遵循JSONProtocol协议,这个JSONProtocol中定义的就是我的propertyMapper协议函数
  2. @interface PJXUserPropertyMapper : NSObject <JSONProtocol>
  3.  
  4. @property (nonatomic, copy) NSString* username; // 用户名
  5. @property (nonatomic, copy) NSString* password; // 密码
  6. @property (nonatomic, copy) NSString* avatarImageURL; // 头像的URL地址
  7.  
  8. @end
  9.  
  10. @implementation PJXUserPropertyMapper
  11. // 实现propertyMapper这个协议方法
  12. + (NSDictionary *)propertyMapper
  13. {
  14. return @{@"Username" : @"username",
  15. @"Password" : @"password",
  16. @"AvatarImageURL" : @"avatarImageURL"};
  17. }
  18.  
  19. @end

随后我定义了一个example。

  1. #pragma mark - PropertyMapper Sample
  2. - (void)runPropertyMapperSample
  3. {
  4. NSDictionary *userDict = @{@"Username" : @"shuaige",
  5. @"Password" : @"",
  6. @"AvatarImageURL" : @"http://www.example.com/shuaige.png"};
  7. PJXUserPropertyMapper *user = [[PJXUserPropertyMapper alloc] initWithJSONData:userDict];
  8.  
  9. NSLog(@"runPropertyMapperSample\n");
  10. NSLog(@"----------------------------------------");
  11. NSLog(@"username:%@\n",user.username);
  12. NSLog(@"password:%@\n",user.password);
  13. NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
  14. }

是不是感觉调用上和之前的非property映射没什么区别?那是因为我们需要在initWithJSONData中增加一些东西。

具体的做法是在PropertyWithDictionary函数增加了一个查表操作。

  1. // 注意我传入的dictionary就是用户提供的JSON数据
  2. // 比如此处传入的key==@"username",value==@"shuaige"
  3. static void PropertyWithDictionaryFunction(const void *key, const void *value, void *context)
  4. {
  5. NSString *keyStr = (__bridge NSString *)(key);
  6.  
  7. ......
  8.  
  9. // 如果使用了JSONProtocol,并且自定义了propertyMapper,那么还需要将keyStr转化下
  10. if ([modelSelf conformsToProtocol:@protocol(JSONProtocol)] && [[modelSelf class] respondsToSelector:@selector(propertyMapper)]) {
  11. keyStr = [[[modelSelf class] propertyMapper] objectForKey:keyStr];
  12. }
  13.  
  14. ......
  15. }

这样就可以啦.我们看看效果:

2.3 没有考虑JSON数据的value值不一定是NSString类型

开始的时候,挺担心我这种写法会不会不兼容别的数据类型。不过我觉得应该没什么问题,毕竟我使用的setter方法本质上没啥问题,我的类型全用id来代替了(事实上,我的想法大错特错):

  1. ((void (*)(id, SEL, id))(void *) objc_msgSend)(modelSelf, info.setter, setValue);

不过本着不怕一万,就怕万一的心态。我还是做了一个example来试验一下:

  1. @interface PJXUserVariousType : NSObject
  2.  
  3. @property (nonatomic, copy) NSString *blogTitle; // 博客标题
  4. @property (nonatomic, strong) NSURL *blogURL; // 博客网址
  5. @property (nonatomic, assign) NSInteger blogIndex; // 博客索引值
  6. @property (nonatomic, strong) NSDate *postDate; // 博客发布时间
  7. @property (nonatomic, strong) NSArray *friends; // 我的好友名称
  8. @property (nonatomic, strong) NSSet *collections; // 我的收藏
  9.  
  10. @end
  11.  
  12. @implementation PJXUserVariousType
  13.  
  14. @end
  15.  
  16. #pragma mark - VariousType Sample
  17. - (void)runVariousTypeSample
  18. {
  19. NSDictionary *userDict = @{@"blogTitle" : @"iOS developer",
  20. @"blogURL" : @"http://www.example.com/blog.html",
  21. @"blogIndex" : @,
  22. @"postDate" : [NSDate date],
  23. @"friends" : @[@"meinv1", @"meinv2", @"meinv3"],
  24. @"collections" : @[@"shuaige1", @"shuaige2", @"shuaige3"]};
  25. PJXUserVariousType *user = [[PJXUserVariousType alloc] initWithJSONData:userDict];
  26.  
  27. NSLog(@"runVariousTypeSample\n");
  28. NSLog(@"----------------------------------------");
  29. NSLog(@"blogTitle:%@\n",user.blogTitle);
  30. NSLog(@"blogURL:%@\n",user.blogURL);
  31. NSLog(@"blogIndex:%ld\n",user.blogIndex);
  32. NSLog(@"postDate:%@\n",user.postDate);
  33. NSLog(@"friends:%@\n",user.friends);
  34. NSLog(@"collections:%@\n",user.collections);
  35. }

你猜输出啥?

其他都正确,唯独我们的blogIndex出错了。这里确实是我欠考虑了,类似NSInteger,BOOL这些NSNumber类型(我暂时只考虑这些常用类型)需要单独处理一下。这一部分看起来容易,但是为了处理这种特殊情况确实要下很大功夫。比如你得先判断该属性是不是double或int这种类型,只有判断除了该属性是double还是int,你才能正确使用setter方法,而此处的调用方式也要单独写一个,因为和之前调用方式有一些些区别,需要判断Number的类型是double,是int,还是BOOl…….

对此我在PJXPropertyInfo中定义了两个函数,一个叫isNumber,用来判断该属性是不是一个Number,另一个叫setNumberValue:withModelSelf:,用来给是Number类型的属性赋值。另外,我仿照YYModel(比YYModel简化很多了)建了一个PJXEncodingType的enum类型,用来存储Number的类型(int?double?BOOL?……),与之配套的还有一个PJXGetEncodingType函数,来获取当前属性的类型(是int?double?BOOL?),具体怎么做还挺复杂的,后面会详细说明。

代码如下:

  1. // Number类型
  2. typedef NS_ENUM(NSUInteger, PJXEncodingType) {
  3. PJXEncodingTypeUnknown = , ///< unknown
  4. PJXEncodingTypeBool = 1, ///< bool
  5. PJXEncodingTypeInt8 = 2, ///< char / BOOL
  6. PJXEncodingTypeUInt8 = 3, ///< unsigned char
  7. PJXEncodingTypeInt16 = 4, ///< short
  8. PJXEncodingTypeUInt16 = 5, ///< unsigned short
  9. PJXEncodingTypeInt32 = 6, ///< int
  10. PJXEncodingTypeUInt32 = 7, ///< unsigned int
  11. PJXEncodingTypeInt64 = 8, ///< long long
  12. PJXEncodingTypeUInt64 = 9, ///< unsigned long long
  13. PJXEncodingTypeFloat = 10, ///< float
  14. PJXEncodingTypeDouble = 11, ///< double
  15. PJXEncodingTypeLongDouble = 12, ///< long double
  16. };
  17.  
  18. // 根据objc_property_attribute_t可以获取到property的类型PJXEncodingType
  19. // 参考YYModel
  20. PJXGetEncodingType(const char *encodingType) {
  21. char *type = (char *)encodingType;
  22. if (!type) return PJXEncodingTypeUnknown;
  23. size_t len = strlen(type);
  24. if (len == ) return PJXEncodingTypeUnknown;
  25.  
  26. switch (*type) {
  27. case 'B': return PJXEncodingTypeBool;
  28. case 'c': return PJXEncodingTypeInt8;
  29. case 'C': return PJXEncodingTypeUInt8;
  30. case 's': return PJXEncodingTypeInt16;
  31. case 'S': return PJXEncodingTypeUInt16;
  32. case 'i': return PJXEncodingTypeInt32;
  33. case 'I': return PJXEncodingTypeUInt32;
  34. case 'l': return PJXEncodingTypeInt32;
  35. case 'L': return PJXEncodingTypeUInt32;
  36. case 'q': return PJXEncodingTypeInt64;
  37. case 'Q': return PJXEncodingTypeUInt64;
  38. case 'f': return PJXEncodingTypeFloat;
  39. case 'd': return PJXEncodingTypeDouble;
  40. case 'D': return PJXEncodingTypeLongDouble;
  41.  
  42. default: return PJXEncodingTypeUnknown;
  43. }
  44. }
  45.  
  46. /**
  47. * @brief 存储Model中每个property的信息
  48. * ......
  49. * @param type 是一个PJXEncodingType类型变量,为了存储该属性是哪种Number(int?double?BOOL?)
  50. */
  51. @interface PJXPropertyInfo : NSObject
  52. ......
  53. @property (nonatomic, assign) PJXEncodingType type;
  54. @end
  55.  
  56. @implementation PJXPropertyInfo
  57.  
  58. - (instancetype)initWithPropertyInfo:(objc_property_t)property
  59. {
  60. self = [self init];
  61.  
  62. if (self) {
  63. ......
  64.  
  65. // 判断属性类型
  66. unsigned int attrCount;
  67. // 关于objc_property_attribute_t,这里有一篇文章介绍的很好
  68. // http://www.henishuo.com/runtime-property-ivar/
  69. objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
  70. for (unsigned int i = ; i < attrCount; i++) {
  71. switch (attrs[i].name[]) {
  72. case 'T': {// EncodingType
  73. if (attrs[i].value) {
  74. //NSLog(@"attrs[%d].value = %s", i, attrs[i].value);
  75. // 可以根据value获取到property类型
  76. _type = PJXGetEncodingType(attrs[i].value);
  77. }
  78. break;
  79. }
  80. default:
  81. break;
  82. }
  83. }
  84. ......
  85. }
  86.  
  87. return self;
  88. }
  89.  
  90. // 根据propertyInfo中存储的type判断其是否为Number
  91. - (BOOL)isNumber
  92. {
  93. switch (self.type) {
  94. case PJXEncodingTypeBool:
  95. case PJXEncodingTypeInt8:
  96. case PJXEncodingTypeUInt8:
  97. case PJXEncodingTypeInt16:
  98. case PJXEncodingTypeUInt16:
  99. case PJXEncodingTypeInt32:
  100. case PJXEncodingTypeUInt32:
  101. case PJXEncodingTypeInt64:
  102. case PJXEncodingTypeUInt64:
  103. case PJXEncodingTypeFloat:
  104. case PJXEncodingTypeDouble:
  105. case PJXEncodingTypeLongDouble:
  106. return YES;
  107. default:
  108. return NO;
  109. break;
  110. }
  111. }
  112.  
  113. // 使用objc_msgSend调用modelSelf中该属性对应的setter方法
  114. - (void)setNumberValue:(NSNumber *)number withModelSelf:(id)modelSelf
  115. {
  116. switch (self.type) {
  117. case PJXEncodingTypeBool:
  118. ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.boolValue);
  119. break;
  120. case PJXEncodingTypeInt8:
  121. ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.charValue);
  122. break;
  123. case PJXEncodingTypeUInt8:
  124. ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedCharValue);
  125. break;
  126. case PJXEncodingTypeInt16:
  127. ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.shortValue);
  128. break;
  129. case PJXEncodingTypeUInt16:
  130. ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedShortValue);
  131. break;
  132. case PJXEncodingTypeInt32:
  133. ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.intValue);
  134. break;
  135. case PJXEncodingTypeUInt32:
  136. ((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedIntValue);
  137. break;
  138. case PJXEncodingTypeInt64:
  139. ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(modelSelf, self.setter, number.longLongValue);
  140. break;
  141. case PJXEncodingTypeUInt64:
  142. ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedLongLongValue);
  143. break;
  144. case PJXEncodingTypeFloat:
  145. ((void (*)(id, SEL, float))(void *) objc_msgSend)(modelSelf, self.setter, number.floatValue);
  146. break;
  147. case PJXEncodingTypeDouble:
  148. ((void (*)(id, SEL, double))(void *) objc_msgSend)(modelSelf, self.setter, number.doubleValue);
  149. break;
  150. case PJXEncodingTypeLongDouble:
  151. ((void (*)(id, SEL, long double))(void *) objc_msgSend)(modelSelf, self.setter, number.doubleValue);
  152. break;
  153. default:
  154. break;
  155. }
  156. }
  157.  
  158. @end

有了上述的几个方法,后面就好办了,只需在PropertyWithDictionaryFunction函数中添加一个Number的判断就行:

  1. static void PropertyWithDictionaryFunction(const void *key, const void *value, void *context)
  2. {
  3. ......
  4.  
  5. // 如果该属性是Number,那么就用Number赋值方法给其赋值
  6. if ([info isNumber]) {
  7. [info setNumberValue:setValue withModelSelf:modelSelf];
  8. } else {
  9. ((void (*)(id, SEL, id))(void *) objc_msgSend)(modelSelf, info.setter, setValue);
  10. }
  11. }

这下终于成功了:

2.4 没有考虑用户自定义了Model属性的setter方法

这个其实比较简单,只需要对property的attribute(objc_property_attribute_t)进行判断即可:

  1. - (instancetype)initWithPropertyInfo:(objc_property_t)property
  2. {
  3. ......
  4.  
  5. BOOL isCustomSetter = NO;
  6. // 判断属性类型
  7. unsigned int attrCount;
  8. // 关于objc_property_attribute_t,这里有一篇文章介绍的很好
  9. // http://www.henishuo.com/runtime-property-ivar/
  10. objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
  11. for (unsigned int i = ; i < attrCount; i++) {
  12. switch (attrs[i].name[]) {
  13. case 'T': { // EncodingType
  14. if (attrs[i].value) {
  15. //NSLog(@"attrs[%d].value = %s", i, attrs[i].value);
  16. // 可以根据value获取到property类型
  17. _type = PJXGetEncodingType(attrs[i].value);
  18. }
  19. break;
  20. }
  21. case 'S': { // 自定义setter方法
  22. if (attrs[i].value) {
  23. isCustomSetter = YES;
  24. _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
  25. }
  26. } break;
  27. default:
  28. break;
  29. }
  30. }
  31.  
  32. if (!isCustomSetter) {
  33. // 如果没有自定义setter方法,只考虑系统默认生成setter方法
  34. // 也就是说属性username的setter方法为setUsername:
  35. NSString *setter = [NSString stringWithFormat:@"%@%@", [_name substringToIndex:].uppercaseString, [_name substringFromIndex:]];
  36. _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@:", setter]);
  37. }
  38. }
  39.  
  40. return self;
  41. }

使用下面这个例子测试:

  1. @interface PJXUserCustomSetter : NSObject
  2.  
  3. @property (nonatomic, copy, setter=setCustomUserName:) NSString* username; // 用户名
  4. @property (nonatomic, copy, setter=setCustomBirthday:) NSDate* birthday; // 生日
  5.  
  6. @end
  7.  
  8. @implementation PJXUserCustomSetter
  9.  
  10. - (void)setCustomUserName:(NSString *)username
  11. {
  12. _username = [NSString stringWithFormat:@"My name is %@", username];
  13. }
  14.  
  15. - (void)setCustomBirthday:(NSDate *)birthday
  16. {
  17. NSTimeInterval timeInterval = **; // 过一天
  18. _birthday = [NSDate dateWithTimeInterval:timeInterval sinceDate:birthday];
  19. }
  20.  
  21. @end
  22.  
  23. #pragma mark - Custom Setter Sample
  24. - (void)runCustomSetterSample
  25. {
  26. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  27. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  28. NSDate *birthday = [dateFormatter dateFromString:@"2016-04-07 00:20:03"];
  29. NSDictionary *userDict = @{@"username" : @"shuaige",
  30. @"birthday" : birthday};
  31. PJXUserCustomSetter *user = [[PJXUserCustomSetter alloc] initWithJSONData:userDict];
  32.  
  33. NSLog(@"runCustomSetterSample\n");
  34. NSLog(@"----------------------------------------");
  35. NSLog(@"username:%@\n",user.username);
  36. NSLog(@"birthday:%@\n",user.birthday);
  37. }

得到的结果为:


成功了.

2.5 没有考虑用户传入的JSON数据有嵌套

我个人感觉这个应该没什么问题,为什么这么说呢?因为我嵌套的无非也是一个NSObject类型,那么就调用其自身的setter方法就OK啊.不过还是以防万一,我构造了一下案例:

  1. @interface PJXBlog : NSObject
  2.  
  3. @property (nonatomic, copy) NSString *title; // 博客名称
  4. @property (nonatomic, strong) NSDate *postDate; // 博客发表日期
  5. @property (nonatomic, copy) PJXUser *author; // 博客作者
  6.  
  7. @end
  8.  
  9. @implementation PJXBlog
  10.  
  11. @end
  12.  
  13. #pragma mark - Nest Sample
  14. - (void)runNestSample
  15. {
  16. NSDictionary *blogDict = @{@"title" : @"how to convert JSON to Model?",
  17. @"postDate" : [NSDate date],
  18. @"author" : @{@"username" : @"shuaige",
  19. @"password" : @"",
  20. @"avatarImageURL":@"http://www.example.com/shuaige.png"}};
  21. PJXBlog *blog = [[PJXBlog alloc] initWithJSONData:blogDict];
  22.  
  23. NSLog(@"runNestSample\n");
  24. NSLog(@"----------------------------------------");
  25. NSLog(@"title:%@\n",blog.title);
  26. NSLog(@"postDate:%@\n",blog.postDate);
  27. NSLog(@"author:%@\n",blog.author);
  28. }

输出结果如下:

结果没什么问题.不过这样说可能不是很负责任,但是目前我也想不到反例.暂时先当做成功了.

3. 总结


以我的能力,目前只能将JSON转化Model实现到这个地步了.总体来说,实现的难度不是很大(因为我考虑的情况还是比较少的,另外还有些功能没添加),不过涉及的知识点还是挺多的,挺不错的一个练手项目:).

附上GitHub地址

【疯狂造轮子-iOS】JSON转Model系列之二的更多相关文章

  1. 【疯狂造轮子-iOS】JSON转Model系列之一

    [疯狂造轮子-iOS]JSON转Model系列之一 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 之前一直看别人的源码,虽然对自己提升比较大,但毕竟不是自己写的,很容易遗 ...

  2. Mac iOS Json 操作Model to JSON

    在移动网络时代,json成为了主流的数据交换格式.如何能够方便快捷的创建.转化.传递json文件称为了开发者必备的技能.幸好,我们生活在开源时代,很多功能不需要我们重现造轮子.今天我推荐一款开源jso ...

  3. “造轮运动”之 ORM框架系列(二)~ 说说我心目中的ORM框架

    ORM概念解析 首先梳理一下ORM的概念,ORM的全拼是Object Relation Mapping (对象关系映射),其中Object就是面向对象语言中的对象,本文使用的是c#语言,所以就是.ne ...

  4. ios json转model的简单现实

    在android开发中,可用第三方的转换库如gson等.当然在ios也有一些库如MJExtensiond等.在这里,我简单实现一下. 一.先建一个model并且继承NSObject,代码如下: cla ...

  5. 重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关

    重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关 引言 重复造轮子系列是自己平时的一些总结.有的轮子依赖社区提供的轮子为基础,这里把使用过程的一些觉得有意思的做个分享.有些思路或者方法在 ...

  6. 造轮子系列之RPC 1:如何从零开始开发RPC框架

    前言 RPC 框架是后端攻城狮永远都绕不开的知识点,目前业界比较知名有 Dubbo.Spring Cloud 等.很多人都停留在了只会用的阶段,作为程序猿,拥有好奇心深入学习,才能有效提高自己的竞争力 ...

  7. GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。

    1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...

  8. 自己造轮子系列之OOM框架AutoMapper

    [前言] OOM框架想必大家在Web开发中是使用频率非常之高的,如果还不甚了解OOM框架,那么我们对OOM框架稍作讲解. OOM顾名思义,Object-Object-Mapping实体间相互转换.常见 ...

  9. 重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印

    重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印 一.引言 桌面端系统经常需要对接各种硬件设备,比如扫描器.读卡器.打印机等. 这里介绍下桌面端 ...

随机推荐

  1. 防御XSS攻击-encode用户输入内容的重要性

    一.开场先科普下XSS 跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS.恶 ...

  2. 关于Unity3D自定义编辑器的学习

    被人物编辑器折腾了一个月,最终还是交了点成品上去(还要很多优化都还么做).  刚接手这项工作时觉得没概念,没想法,不知道.后来就去看<<Unity5.X从入门到精通>>中有关于 ...

  3. C#高性能TCP服务的多种实现方式

    哎~~ 想想大部分园友应该对 "高性能" 字样更感兴趣,为了吸引眼球所以标题中一定要突出,其实我更喜欢的标题是<猴赛雷,C#编写TCP服务的花样姿势!>. 本篇文章的主 ...

  4. Linux CentOS 配置JDK环境

    一.下载JDK 下载JDK的方式有两种: 1.Linux中使用wget下载 1.使用命令安装wget yum install wget 2.下载 wget 'http://download.oracl ...

  5. iOS二维码生成、识别、扫描等

    二维码扫描 前言: 最近的项目中使用到了二维码,二维码这个模块功能也完成:觉得还是有必要总结一下用来做记录.好长时间没有写二维码了都忘记在差不多了,重新拾起来还是挻快的. 二维码使用场景: 生活中有很 ...

  6. JavaScript的继承实现方式

    1.使用call或apply方法,将父对象的构造函数绑定在子对象上 function A(){ this.name = 'json'; } function B(){ A.call(this); } ...

  7. 一起学微软Power BI系列-使用技巧(3)Power BI安卓手机版安装与体验

    Power BI有手机版,目前支持安卓,苹果和WP,不过没有WP手机,苹果在国内还不能用,要FQ和用就不测试了.安卓的我也也是费了九牛二虎之力才把app下载下来,把方法分享给大家. FQ太麻烦,所以建 ...

  8. 主成分分析(PCA)原理总结

    主成分分析(Principal components analysis,以下简称PCA)是最重要的降维方法之一.在数据压缩消除冗余和数据噪音消除等领域都有广泛的应用.一般我们提到降维最容易想到的算法就 ...

  9. C#语法糖大汇总

    首先需要声明的是"语法糖"这个词绝非贬义词,它可以给我带来方便,是一种便捷的写法,编译器会帮我们做转换:而且可以提高开发编码的效率,在性能上也不会带来损失.这让java开发人员羡慕 ...

  10. 用angular怎么缓存父页面数据

    angular做单页面应用是一个比较好的框架,但是它有一定的入门难度,对于新手来说可能会碰到很多坑,也有许多难题,大部分仔细看文档,找社区是能解决的. 但有些问题也许资料比较少,最近遇到过一个要缓存父 ...