iOS中复杂实体类对象的json序列化

来源:互联网 发布:单片机编程器 编辑:程序博客网 时间:2024/06/03 15:03

由于项目需要在做ios客户端时,与服务器通信是基于webservice的,而且数据交互都是使用json序列化后的字符串,这样就只需要双方解析json字符串为一个实体类就可以得到所需要的数据(至于与xml通信方式比较的优劣我在此不评论)。

而这种方式在java服务器端以及我之前做的android客户端都有成熟的框架,可以对自定义的复杂实体类进行json序列化以及对json字符串反序列化为目标实体类对象!在ios端貌似没发现(如果有谁知道麻烦指点下啊!),而且本人刚入门ios开发,基本语法都不怎么了解,查找了好多资料也无头绪,对于自定义实体类的序列化工具也没找到,更别说反序列化工具了。

摸索几天后,借鉴网上以为仁兄发布的序列化普通实体类的方法才得以实现了我所需要的功能,引用下这位兄弟的成果, http://www.cocoachina.com/bbs/read.php?tid=86667&page=1 他是利用了obj c runtime将简单的自定义实体类对象转为了json字符串,但我是纯新手对obj c runtime之类的不怎么了解,我也没时间深究咯。

先说下我对于ios中生成json字符串的理解,ios中利用了NSDictionary这种类型,先将一个个键值对构造成一个NSDictionary对象,然后再利用NSJSONSerialization 将其转为NSData类型数据,之后将其转为NSString字符串,这样便可以生成json格式的字符串了。

在详细查看了上面那位前辈的代码后,发现对于项目中的许多普通实体类的序列化也算减轻了不少工作量,但是对于复杂的嵌套实体类这种方式就失败了,更别说里面还包含NSArray的实体类数组名了,而我们的项目就刚好需要如此,上报数据时本来就包含了很多复杂字段的。

于是只好将复杂实体类中嵌套实体类先序列化后再用最基本的方法构造键值对,生成NSDictionary。最后我将生成的字符串打印出来,问题来了,下面是我得到的字符串:
{"customerName":"haoero","location":{\"longitude\":50.98988,\"address\":\"武汉华科\",\"latitude\":100.9899},"customerId":50,"customerNo":"A110"}

注意观察就可以发现这已经不符合json字符串的格式了,中间多了转义符\ 。因为在对嵌套的实体类 Location序列化后得到的json字符串应该是{"longitude":50.98988,"address":"武汉华科","latitude":100.9899},这是用前辈的方式得到的,本身没错,但当把他们赋值给NSDictionary的location这个key后,这里便会把里面有引号的地方来了个转义符,因为每个key和value的最外层都已经有了一个"",由此造成了冲突,当然在此可以一个个去把最终的字符串去掉转义符,只是这样带来的不便也可想而知。

另外当遇到有包含NSArray的实体类数组的情况时,这样一个个构造时,还要加入[]来包含里面的各个实体类字符串,这样也会是非常的麻烦的;于是我想干脆把前辈的方法重写一下,只是像java里反序列化时传入嵌套实体类的类型class,这样就可以利用已有的方法加上一个递归调用,便可以实现对于复杂嵌套实体类的序列化了,下面是我改写前辈那个方法后的代码(希望前辈谅解):
//
//  serializeObjectWithChildObjectsAndComplexArray
//  参数:complexObject, 需要序列化的复杂实体类对象
//  参数:childSimpleClasses,所有嵌套的简单实体类的类型,形如【NSString class】
//  参数:childSimpleClassInArray,所有嵌套在数组里的简单实体类类型,形如【NSString class】
//  Created by haoero on 2012-10
// 
//
- (NSString *)serializeObjectWithChildObjectsAndComplexArray:(id)complexObject :(NSArray*)childSimpleClasses :(NSArray*) childSimpleClassInArray
{
    NSString *complexClassName = NSStringFromClass([complexObject class]);

    const char *cComplexClassName = [complexClassName UTF8String];
    id theComplexClass = objc_getClass(cComplexClassName);

    unsigned int outCount, i;
//获取当前实体类中所有属性名
    objc_property_t *properties = class_copyPropertyList(theComplexClass, &outCount);

//存放所有普通类型的属性变量名
    NSMutableArray *propertyNames = [[NSMutableArray alloc] initWithCapacity:1];
//存放所有数组类型的属性变量名
    NSMutableArray *childArrayPropertyNames = [[NSMutableArray alloc] initWithCapacity:1];
//存放所有数组类型变量对应的JSON字符串的
    NSMutableArray *childArrayJSONString  = [[NSMutableArray alloc] initWithCapacity:1];

//存放所有数组类型的属性变量名
    NSMutableArray *childClassesPropertyNames = [[NSMutableArray alloc] initWithCapacity:1];
//存放所有数组类型变量对应的JSON字符串的
    NSMutableArray *childClassesJSONString  = [[NSMutableArray alloc] initWithCapacity:1];

    for (i = 0; i < outCount; i++) {

        objc_property_t property = properties;

NSString *propertyNameString =[[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding] ;

        SEL tempSelector = NSSelectorFromString(propertyNameString);
        id tempValue = [complexObject performSelector:tempSelector];


//如果目标实体类包含了嵌套实体类或者子实体类数组
        if (childSimpleClassInArray!=nil || childSimpleClasses!=nil) {

//如果当前属性类型是数组
            if ([tempValue isKindOfClass:[NSArray class]]) {
                NSArray *tempChildArray = (NSArray*)tempValue; 
                if ([tempChildArray count]>0) {
                    //判断当前数组是否是目标类型数组
                    int flag = 0;
                    for (int j=0; j<[childSimpleClassInArray count]; j++) {
                        //如果是目标类型数组,则改变flag的值
                        if ([[tempChildArray objectAtIndex:0] isKindOfClass:[childSimpleClassInArray objectAtIndex:j]]) {
                            flag = 1;
//则将属性变量名添加到childArrayPropertyNames
                            [childArrayPropertyNames addObject:propertyNameString];

                            NSMutableString *tempChildString = [[NSMutableString alloc] init];
                            for (int m=0; m< [tempChildArray count]; m++) {

                                if (m == 0) {
                                    [tempChildString appendFormat:@"["];
                                }

//先序列化数组内单独, 在此可以针对不同情况修改第二,三个参数来递归调用
                                NSString *singleJSONStringInArray = [[SerializationComplexEntities sharedSerializer] serializeObjectWithChildObjectsAndComplexArray:[tempChildArray objectAtIndex:m] :childSimpleClasses :childSimpleClassInArray];

                                if (singleJSONStringInArray!=nil) {
                                    [tempChildString appendFormat:singleJSONStringInArray];
                                }

                                if (m!= [tempChildArray count]-1) {
                                    [tempChildString appendFormat:@","];
                                }
                                else {
                                    [tempChildString appendFormat:@"]"];
                                }

                            }
                            //添加到存放子数组JSON字符串的数组里
                            [childArrayJSONString addObject:tempChildString]; 
                        }
                    }
                    //如果是普通类型的数组
                    if (flag == 0) {
//则将属性变量名添加到propertyNames
                        [propertyNames addObject:propertyNameString];
                    }
                }

            } 
//如果当前属性类型是除数组外的类型
            else {
                int isChildClass = 0;
                for (int p = 0; p < [childSimpleClasses count]; p++) {
                    //判断是否是目标嵌套实体类
                    if ([tempValue isKindOfClass:[childSimpleClasses objectAtIndex:p ]]) {                       
                        isChildClass = 1;
                        [childClassesPropertyNames addObject:propertyNameString];


//先序列化嵌套实体类, 在此可以针对不同情况修改第二个参数来递归调用
                        NSString *singleJSONStringInChildClasses = [[SerializationComplexEntities sharedSerializer] serializeObjectWithChildObject:tempValue:nil];

                        //添加到存放嵌套实体类的JSON字符串的数组里
                        [childClassesJSONString addObject:singleJSONStringInChildClasses];

                    }
                }
//如果当前属性类型除数组和目标嵌套子实体类外的普通类型
                if (isChildClass==0) {
                    [propertyNames addObject:propertyNameString];
                }

            }

        }                
//如果目标实体类不包含子实体类数组,则直接添加变量名
        else {
            [propertyNames addObject:propertyNameString];
        }
    }

//开始构造Dictionary
NSMutableDictionary *finalDict = [[NSMutableDictionary alloc] initWithCapacity:1];

//对所有子实体类数组
    for(NSString *key in childArrayPropertyNames)
    {
//对数组变量的键值先暂时用一个字符串代替
        [finalDict setObject:[[NSString alloc] initWithFormat:@"NULL%@NULL",key] forKey:key];
    }

//对所有子实体类数组
    for(NSString *key in childClassesPropertyNames)
    {
//对数组变量的键值先暂时用一个字符串代替
        [finalDict setObject:[[NSString alloc] initWithFormat:@"NULL%@NULL",key] forKey:key];
    }

    for(NSString *key in propertyNames)
    {
        SEL selector = NSSelectorFromString(key);
        id value = [complexObject performSelector:selector];

        if (value == nil)
        {
            value = [NSNull null];
        }

        [finalDict setObject:value forKey:key];
    }
//没替换字符串的初始JSON字符串
NSMutableString *jsonString =[[NSMutableString alloc] initWithString: [[CJSONSerializer serializer] serializeDictionary:finalDict]];

//将数组变量的键值字符串替换成之前序列化的字符串
    for(int m=0; m<childArrayJSONString.count; m++)
    {
        if ([childArrayJSONString objectAtIndex:m] !=nil) {

            NSString *tempStringToBeReplaced = [[NSString alloc] initWithFormat:@"\"NULL%@NULL\"",[childArrayPropertyNames objectAtIndex:m]];           
            jsonString = [[NSMutableString alloc] initWithString:[jsonString stringByReplacingOccurrencesOfString:tempStringToBeReplaced withString:[childArrayJSONString objectAtIndex:m]]];

        }     

    }

//将数组变量的键值字符串替换成之前序列化的字符串
    for(int m=0; m<childClassesJSONString.count; m++)
    {
        if ([childClassesJSONString objectAtIndex:m] !=nil) {

            NSString *tempStringToBeReplaced = [[NSString alloc] initWithFormat:@"\"NULL%@NULL\"",[childClassesPropertyNames objectAtIndex:m]];           
            jsonString = [[NSMutableString alloc] initWithString:[jsonString stringByReplacingOccurrencesOfString:tempStringToBeReplaced withString:[childClassesJSONString objectAtIndex:m]]];

        }     

    }

    return jsonString;

}

当然我这里用了一个比较笨的方法,是先将复杂嵌套实体类属性对应的value先赋值为一个特殊的字符串,之后再将其替换即可,当然这只是小弟的无奈之举,也算实现了基本的要求,还有效率方面的问题由于项目赶得紧,还没有测试,以后再加上吧。

 

 

社区原帖:http://www.cocoachina.com/bbs/read.php?tid=125232&fpage=4