【疯狂造轮子-iOS】JSON转Model系列之二
【疯狂造轮子-iOS】JSON转Model系列之二
本文转载请注明出处 —— polobymulberry-博客园
1. 前言
上一篇《【疯狂造轮子-iOS】JSON转Model系列之一》实现了一个简陋的JSON转Model的库,不过还存在很多问题。下面我会尝试一个个去解决。
2. 存在问题及解决思路
2.1 没有考虑JSON数据并不一定是NSDictionary类型
有时候JSON并不一定是NSDictionary类型,可能是一个字符串,也可能是NSData类型的数据。不过不管是哪种类型,统统先将其转化为NSData数据,然后使用+[NSJSONSerialization JSONObjectWithData:options:error:]来转化。所以我在initWithAttributes:上面又封装了一层。
- (instancetype)initWithJSONData:(id)json
{
NSDictionary *dict = [self pjx_dictionaryWithJSON:json];
return [self initWithAttributes:dict];
} /**
* @brief 将NSString和NSData格式的json数据转化为NSDictionary类型
*/
- (NSDictionary *)pjx_dictionaryWithJSON:(id)json
{
if (!json) {
return nil;
}
// 若是NSDictionary类型,直接返回
if ([json isKindOfClass:[NSDictionary class]]) {
return json;
} NSDictionary *dict = nil;
NSData *jsonData = nil; if ([json isKindOfClass:[NSString class]]) {
// 如果是NSString,就先转化为NSData
jsonData = [(NSString*)json dataUsingEncoding:NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
} if (jsonData && [jsonData isKindOfClass:[NSData class]]) {
// 如果时NSData类型,使用NSJSONSerialization
NSError *error = nil;
dict = [NSJSONSerialization JSONObjectWithData:jsonData options: error:&error];
if (error) {
NSLog(@"pjx_dictionaryWithJSON error:%@", error);
return nil;
}
if (![dict isKindOfClass:[NSDictionary class]]) {
return nil;
}
} return dict;
}
为此,我在ViewController添加了两个sample。分别用来解析NSString类型的JSON数据和NSData类型的JSON数据。
// NSString类型的JSON数据
- (void)runSimpleSample2
{
NSString *userStr = @" \
{ \
\"username\" : \"shuaige\", \
\"password\" : \"123456\", \
\"avatarImageURL\" : \"http://www.example.com/shuaige.png\" \
}"; PJXUser *user = [[PJXUser alloc] initWithJSONData:userStr]; NSLog(@"runSimpleSample2\n");
NSLog(@"----------------------------------------");
NSLog(@"username:%@\n",user.username);
NSLog(@"password:%@\n",user.password);
NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
} // NSData类型的JSON数据
- (void)runSimpleSample3
{
NSString *userInfoFilePath = [[NSBundle mainBundle] pathForResource:@"UserInfo" ofType:@"txt"];
NSData *data = [NSData dataWithContentsOfFile:userInfoFilePath];
PJXUser *user = [[PJXUser alloc] initWithJSONData:data]; NSLog(@"runSimpleSample3\n");
NSLog(@"----------------------------------------");
NSLog(@"username:%@\n",user.username);
NSLog(@"password:%@\n",user.password);
NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
}
输出结果也是正确的:
2.2 没有考虑用户传入的JSON数据的key值和property的名称不一致
我第一反应是使用一个映射表。也就是说用户使用时需要自定义一套property和key的映射表。YYModel中使用了一个+ (NSDictionary *)modelCustomPropertyMapper函数,用户可以自定义该函数达到映射表的效果,而这个函数是放在一个protocol中的。我挺认同这种设计的,因为modelCustomPropertyMapper这种函数和Model是一种组合关系,可有可无(optional),所以设计成协议更合适。但是作者在设计protocol又说了一句:
// There's no need to add '<YYModel>' to your class header.
@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协议函数。
// 遵循JSONProtocol协议,这个JSONProtocol中定义的就是我的propertyMapper协议函数
@interface PJXUserPropertyMapper : NSObject <JSONProtocol> @property (nonatomic, copy) NSString* username; // 用户名
@property (nonatomic, copy) NSString* password; // 密码
@property (nonatomic, copy) NSString* avatarImageURL; // 头像的URL地址 @end @implementation PJXUserPropertyMapper
// 实现propertyMapper这个协议方法
+ (NSDictionary *)propertyMapper
{
return @{@"Username" : @"username",
@"Password" : @"password",
@"AvatarImageURL" : @"avatarImageURL"};
} @end
随后我定义了一个example。
#pragma mark - PropertyMapper Sample
- (void)runPropertyMapperSample
{
NSDictionary *userDict = @{@"Username" : @"shuaige",
@"Password" : @"",
@"AvatarImageURL" : @"http://www.example.com/shuaige.png"};
PJXUserPropertyMapper *user = [[PJXUserPropertyMapper alloc] initWithJSONData:userDict]; NSLog(@"runPropertyMapperSample\n");
NSLog(@"----------------------------------------");
NSLog(@"username:%@\n",user.username);
NSLog(@"password:%@\n",user.password);
NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
}
是不是感觉调用上和之前的非property映射没什么区别?那是因为我们需要在initWithJSONData中增加一些东西。
具体的做法是在PropertyWithDictionary函数增加了一个查表操作。
// 注意我传入的dictionary就是用户提供的JSON数据
// 比如此处传入的key==@"username",value==@"shuaige"
static void PropertyWithDictionaryFunction(const void *key, const void *value, void *context)
{
NSString *keyStr = (__bridge NSString *)(key); ...... // 如果使用了JSONProtocol,并且自定义了propertyMapper,那么还需要将keyStr转化下
if ([modelSelf conformsToProtocol:@protocol(JSONProtocol)] && [[modelSelf class] respondsToSelector:@selector(propertyMapper)]) {
keyStr = [[[modelSelf class] propertyMapper] objectForKey:keyStr];
} ......
}
这样就可以啦.我们看看效果:
2.3 没有考虑JSON数据的value值不一定是NSString类型
开始的时候,挺担心我这种写法会不会不兼容别的数据类型。不过我觉得应该没什么问题,毕竟我使用的setter方法本质上没啥问题,我的类型全用id来代替了(事实上,我的想法大错特错):
((void (*)(id, SEL, id))(void *) objc_msgSend)(modelSelf, info.setter, setValue);
不过本着不怕一万,就怕万一的心态。我还是做了一个example来试验一下:
@interface PJXUserVariousType : NSObject @property (nonatomic, copy) NSString *blogTitle; // 博客标题
@property (nonatomic, strong) NSURL *blogURL; // 博客网址
@property (nonatomic, assign) NSInteger blogIndex; // 博客索引值
@property (nonatomic, strong) NSDate *postDate; // 博客发布时间
@property (nonatomic, strong) NSArray *friends; // 我的好友名称
@property (nonatomic, strong) NSSet *collections; // 我的收藏 @end @implementation PJXUserVariousType @end #pragma mark - VariousType Sample
- (void)runVariousTypeSample
{
NSDictionary *userDict = @{@"blogTitle" : @"iOS developer",
@"blogURL" : @"http://www.example.com/blog.html",
@"blogIndex" : @,
@"postDate" : [NSDate date],
@"friends" : @[@"meinv1", @"meinv2", @"meinv3"],
@"collections" : @[@"shuaige1", @"shuaige2", @"shuaige3"]};
PJXUserVariousType *user = [[PJXUserVariousType alloc] initWithJSONData:userDict]; NSLog(@"runVariousTypeSample\n");
NSLog(@"----------------------------------------");
NSLog(@"blogTitle:%@\n",user.blogTitle);
NSLog(@"blogURL:%@\n",user.blogURL);
NSLog(@"blogIndex:%ld\n",user.blogIndex);
NSLog(@"postDate:%@\n",user.postDate);
NSLog(@"friends:%@\n",user.friends);
NSLog(@"collections:%@\n",user.collections);
}
你猜输出啥?
其他都正确,唯独我们的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?),具体怎么做还挺复杂的,后面会详细说明。
代码如下:
// Number类型
typedef NS_ENUM(NSUInteger, PJXEncodingType) {
PJXEncodingTypeUnknown = , ///< unknown
PJXEncodingTypeBool = 1, ///< bool
PJXEncodingTypeInt8 = 2, ///< char / BOOL
PJXEncodingTypeUInt8 = 3, ///< unsigned char
PJXEncodingTypeInt16 = 4, ///< short
PJXEncodingTypeUInt16 = 5, ///< unsigned short
PJXEncodingTypeInt32 = 6, ///< int
PJXEncodingTypeUInt32 = 7, ///< unsigned int
PJXEncodingTypeInt64 = 8, ///< long long
PJXEncodingTypeUInt64 = 9, ///< unsigned long long
PJXEncodingTypeFloat = 10, ///< float
PJXEncodingTypeDouble = 11, ///< double
PJXEncodingTypeLongDouble = 12, ///< long double
}; // 根据objc_property_attribute_t可以获取到property的类型PJXEncodingType
// 参考YYModel
PJXGetEncodingType(const char *encodingType) {
char *type = (char *)encodingType;
if (!type) return PJXEncodingTypeUnknown;
size_t len = strlen(type);
if (len == ) return PJXEncodingTypeUnknown; switch (*type) {
case 'B': return PJXEncodingTypeBool;
case 'c': return PJXEncodingTypeInt8;
case 'C': return PJXEncodingTypeUInt8;
case 's': return PJXEncodingTypeInt16;
case 'S': return PJXEncodingTypeUInt16;
case 'i': return PJXEncodingTypeInt32;
case 'I': return PJXEncodingTypeUInt32;
case 'l': return PJXEncodingTypeInt32;
case 'L': return PJXEncodingTypeUInt32;
case 'q': return PJXEncodingTypeInt64;
case 'Q': return PJXEncodingTypeUInt64;
case 'f': return PJXEncodingTypeFloat;
case 'd': return PJXEncodingTypeDouble;
case 'D': return PJXEncodingTypeLongDouble; default: return PJXEncodingTypeUnknown;
}
} /**
* @brief 存储Model中每个property的信息
* ......
* @param type 是一个PJXEncodingType类型变量,为了存储该属性是哪种Number(int?double?BOOL?)
*/
@interface PJXPropertyInfo : NSObject
......
@property (nonatomic, assign) PJXEncodingType type;
@end @implementation PJXPropertyInfo - (instancetype)initWithPropertyInfo:(objc_property_t)property
{
self = [self init]; if (self) {
...... // 判断属性类型
unsigned int attrCount;
// 关于objc_property_attribute_t,这里有一篇文章介绍的很好
// http://www.henishuo.com/runtime-property-ivar/
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = ; i < attrCount; i++) {
switch (attrs[i].name[]) {
case 'T': {// EncodingType
if (attrs[i].value) {
//NSLog(@"attrs[%d].value = %s", i, attrs[i].value);
// 可以根据value获取到property类型
_type = PJXGetEncodingType(attrs[i].value);
}
break;
}
default:
break;
}
}
......
} return self;
} // 根据propertyInfo中存储的type判断其是否为Number
- (BOOL)isNumber
{
switch (self.type) {
case PJXEncodingTypeBool:
case PJXEncodingTypeInt8:
case PJXEncodingTypeUInt8:
case PJXEncodingTypeInt16:
case PJXEncodingTypeUInt16:
case PJXEncodingTypeInt32:
case PJXEncodingTypeUInt32:
case PJXEncodingTypeInt64:
case PJXEncodingTypeUInt64:
case PJXEncodingTypeFloat:
case PJXEncodingTypeDouble:
case PJXEncodingTypeLongDouble:
return YES;
default:
return NO;
break;
}
} // 使用objc_msgSend调用modelSelf中该属性对应的setter方法
- (void)setNumberValue:(NSNumber *)number withModelSelf:(id)modelSelf
{
switch (self.type) {
case PJXEncodingTypeBool:
((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.boolValue);
break;
case PJXEncodingTypeInt8:
((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.charValue);
break;
case PJXEncodingTypeUInt8:
((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedCharValue);
break;
case PJXEncodingTypeInt16:
((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.shortValue);
break;
case PJXEncodingTypeUInt16:
((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedShortValue);
break;
case PJXEncodingTypeInt32:
((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.intValue);
break;
case PJXEncodingTypeUInt32:
((void (*)(id, SEL, BOOL))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedIntValue);
break;
case PJXEncodingTypeInt64:
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(modelSelf, self.setter, number.longLongValue);
break;
case PJXEncodingTypeUInt64:
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)(modelSelf, self.setter, number.unsignedLongLongValue);
break;
case PJXEncodingTypeFloat:
((void (*)(id, SEL, float))(void *) objc_msgSend)(modelSelf, self.setter, number.floatValue);
break;
case PJXEncodingTypeDouble:
((void (*)(id, SEL, double))(void *) objc_msgSend)(modelSelf, self.setter, number.doubleValue);
break;
case PJXEncodingTypeLongDouble:
((void (*)(id, SEL, long double))(void *) objc_msgSend)(modelSelf, self.setter, number.doubleValue);
break;
default:
break;
}
} @end
有了上述的几个方法,后面就好办了,只需在PropertyWithDictionaryFunction函数中添加一个Number的判断就行:
static void PropertyWithDictionaryFunction(const void *key, const void *value, void *context)
{
...... // 如果该属性是Number,那么就用Number赋值方法给其赋值
if ([info isNumber]) {
[info setNumberValue:setValue withModelSelf:modelSelf];
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)(modelSelf, info.setter, setValue);
}
}
这下终于成功了:
2.4 没有考虑用户自定义了Model属性的setter方法
这个其实比较简单,只需要对property的attribute(objc_property_attribute_t)进行判断即可:
- (instancetype)initWithPropertyInfo:(objc_property_t)property
{
...... BOOL isCustomSetter = NO;
// 判断属性类型
unsigned int attrCount;
// 关于objc_property_attribute_t,这里有一篇文章介绍的很好
// http://www.henishuo.com/runtime-property-ivar/
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = ; i < attrCount; i++) {
switch (attrs[i].name[]) {
case 'T': { // EncodingType
if (attrs[i].value) {
//NSLog(@"attrs[%d].value = %s", i, attrs[i].value);
// 可以根据value获取到property类型
_type = PJXGetEncodingType(attrs[i].value);
}
break;
}
case 'S': { // 自定义setter方法
if (attrs[i].value) {
isCustomSetter = YES;
_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} break;
default:
break;
}
} if (!isCustomSetter) {
// 如果没有自定义setter方法,只考虑系统默认生成setter方法
// 也就是说属性username的setter方法为setUsername:
NSString *setter = [NSString stringWithFormat:@"%@%@", [_name substringToIndex:].uppercaseString, [_name substringFromIndex:]];
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@:", setter]);
}
} return self;
}
使用下面这个例子测试:
@interface PJXUserCustomSetter : NSObject @property (nonatomic, copy, setter=setCustomUserName:) NSString* username; // 用户名
@property (nonatomic, copy, setter=setCustomBirthday:) NSDate* birthday; // 生日 @end @implementation PJXUserCustomSetter - (void)setCustomUserName:(NSString *)username
{
_username = [NSString stringWithFormat:@"My name is %@", username];
} - (void)setCustomBirthday:(NSDate *)birthday
{
NSTimeInterval timeInterval = **; // 过一天
_birthday = [NSDate dateWithTimeInterval:timeInterval sinceDate:birthday];
} @end #pragma mark - Custom Setter Sample
- (void)runCustomSetterSample
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate *birthday = [dateFormatter dateFromString:@"2016-04-07 00:20:03"];
NSDictionary *userDict = @{@"username" : @"shuaige",
@"birthday" : birthday};
PJXUserCustomSetter *user = [[PJXUserCustomSetter alloc] initWithJSONData:userDict]; NSLog(@"runCustomSetterSample\n");
NSLog(@"----------------------------------------");
NSLog(@"username:%@\n",user.username);
NSLog(@"birthday:%@\n",user.birthday);
}
得到的结果为:
成功了.
2.5 没有考虑用户传入的JSON数据有嵌套
我个人感觉这个应该没什么问题,为什么这么说呢?因为我嵌套的无非也是一个NSObject类型,那么就调用其自身的setter方法就OK啊.不过还是以防万一,我构造了一下案例:
@interface PJXBlog : NSObject @property (nonatomic, copy) NSString *title; // 博客名称
@property (nonatomic, strong) NSDate *postDate; // 博客发表日期
@property (nonatomic, copy) PJXUser *author; // 博客作者 @end @implementation PJXBlog @end #pragma mark - Nest Sample
- (void)runNestSample
{
NSDictionary *blogDict = @{@"title" : @"how to convert JSON to Model?",
@"postDate" : [NSDate date],
@"author" : @{@"username" : @"shuaige",
@"password" : @"",
@"avatarImageURL":@"http://www.example.com/shuaige.png"}};
PJXBlog *blog = [[PJXBlog alloc] initWithJSONData:blogDict]; NSLog(@"runNestSample\n");
NSLog(@"----------------------------------------");
NSLog(@"title:%@\n",blog.title);
NSLog(@"postDate:%@\n",blog.postDate);
NSLog(@"author:%@\n",blog.author);
}
输出结果如下:
结果没什么问题.不过这样说可能不是很负责任,但是目前我也想不到反例.暂时先当做成功了.
3. 总结
以我的能力,目前只能将JSON转化Model实现到这个地步了.总体来说,实现的难度不是很大(因为我考虑的情况还是比较少的,另外还有些功能没添加),不过涉及的知识点还是挺多的,挺不错的一个练手项目:).
附上GitHub地址。
【疯狂造轮子-iOS】JSON转Model系列之二的更多相关文章
- 【疯狂造轮子-iOS】JSON转Model系列之一
[疯狂造轮子-iOS]JSON转Model系列之一 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 之前一直看别人的源码,虽然对自己提升比较大,但毕竟不是自己写的,很容易遗 ...
- Mac iOS Json 操作Model to JSON
在移动网络时代,json成为了主流的数据交换格式.如何能够方便快捷的创建.转化.传递json文件称为了开发者必备的技能.幸好,我们生活在开源时代,很多功能不需要我们重现造轮子.今天我推荐一款开源jso ...
- “造轮运动”之 ORM框架系列(二)~ 说说我心目中的ORM框架
ORM概念解析 首先梳理一下ORM的概念,ORM的全拼是Object Relation Mapping (对象关系映射),其中Object就是面向对象语言中的对象,本文使用的是c#语言,所以就是.ne ...
- ios json转model的简单现实
在android开发中,可用第三方的转换库如gson等.当然在ios也有一些库如MJExtensiond等.在这里,我简单实现一下. 一.先建一个model并且继承NSObject,代码如下: cla ...
- 重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关
重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关 引言 重复造轮子系列是自己平时的一些总结.有的轮子依赖社区提供的轮子为基础,这里把使用过程的一些觉得有意思的做个分享.有些思路或者方法在 ...
- 造轮子系列之RPC 1:如何从零开始开发RPC框架
前言 RPC 框架是后端攻城狮永远都绕不开的知识点,目前业界比较知名有 Dubbo.Spring Cloud 等.很多人都停留在了只会用的阶段,作为程序猿,拥有好奇心深入学习,才能有效提高自己的竞争力 ...
- GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。
1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...
- 自己造轮子系列之OOM框架AutoMapper
[前言] OOM框架想必大家在Web开发中是使用频率非常之高的,如果还不甚了解OOM框架,那么我们对OOM框架稍作讲解. OOM顾名思义,Object-Object-Mapping实体间相互转换.常见 ...
- 重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印
重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印 一.引言 桌面端系统经常需要对接各种硬件设备,比如扫描器.读卡器.打印机等. 这里介绍下桌面端 ...
随机推荐
- 从0开始搭建SQL Server AlwaysOn 第一篇(配置域控)
从0开始搭建SQL Server AlwaysOn 第一篇(配置域控) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www.cnb ...
- KV存储系统
现在的KV存储系统都是分布式的,首先介绍Zookeeper——针对大型分布式系统的高可靠的协调系统. 开发分布式系统是件很困难的事情,其中的困难主要体现在分布式系统的“部分失败”.“部分失败”是指信息 ...
- EventBus实现activity跟fragment交互数据
最近老是听到技术群里面有人提出需求,activity跟fragment交互数据,或者从一个activity跳转到另外一个activity的fragment,所以我给大家介绍一个开源项目,EventBu ...
- 利用Node.js的Net模块实现一个命令行多人聊天室
1.net模块基本API 要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用.NET模块API分为两大类:Server和Socket类.工厂方法. Server类 ...
- DDD 领域驱动设计-商品建模之路
最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念: 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独 ...
- linux服务器开发一 基础
注:本文仅限交流使用,请务用于商业用途,否则后果自负! Linux 1.Linux介绍 Linux是类Unix计算机操作系统的统称. Linux操作系统的内核的名字也是“Linux”. Linux这个 ...
- 多本地代码工作点更新到2个远端GIT仓库
摘要:本文介绍了笔者多个本地工作节点(地方)的多台电脑(PC/笔记本电脑)同步源码到2个远端的GIT(一个GITHUB国外强制公开,一个oschina国内可不公开). 作者:太初 转载说明:请指明原作 ...
- ubuntu14 安装及卸载vmware
原帖http://blog.sina.com.cn/s/blog_73dac6b50101gp4f.html 适用于ubuntu14和vmware player 12.5
- XSS 前端防火墙 —— 可疑模块拦截
上一篇介绍的系统,已能预警现实中的大多数 XSS 攻击,但想绕过还是很容易的. 由于是在前端防护,策略配置都能在源代码里找到,因此很快就能试出破解方案.并且攻击者可以屏蔽日志接口,在自己电脑上永不发出 ...
- mono for android 用ISharedPreferences 进行状态保持 会话保持 应用程序首选项保存
由于项目需要 要保持用户登录状态 要进行状态保持 用途就好像asp.net的session一样 登录的时候进行保存 ISharedPreferences shared = GetSharedPrefe ...