• 一种自动的将自定义类序列化为JSON的方法


    最近因为项目需求,需要将一些自定义的类序列化为JSON,网上有很多好用的第三方序列化工具,但都只能自动序列化一些基本类型,如NSNumber,NSString与NSDictionary这种,没有一种第三方工具提供直接将自定义类序列化的方法(至少据我所知:),而对于这种序列化自定义的类的需求,网上能查到的方法只有将自定义的类手动的转存为一个NSDictionary,然后再使用第三方工具来序列化。例如对于一个类Foo,有如下定义:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    @interface Foo : NSObject
     
    {
     
      NSString *_property1;
     
      NSString *_property2;
     
    }
     
    @property(nonatomic,retain)NSString *property1;
     
    @property(nonatomic,retain)NSString *property2;
     
      
     
    @implementation Foo
     
    @synthesize property1 = _property1;
    @synthesize property2 = _property2;
     
    - (id)init
    {
        self = [super init];
         
        if (self)
        {
            _property1 = @"haha";
            _property2 = @"hehe";
        }
     
        return self;
    }
     
    - (void)dealloc
     
    {
     
      [super dealloc];
     
    }


    要序列化它的方法只有:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Foo *foo = [[Foo alloc] init];
     
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
     
                                 foo.property1,@"property1",
     
                                 foo.property2,@"property2",               
     
                                    nil];
     
    [[JSONSerializer serializer] serializer:dict];


    这种方法的缺陷在于太不灵活,每一次序列化的时候都需要写很多重复的代码,上面的代码还没有考虑属性值为nil的情况(因为当属性值为nil时,NSDictionary会认为初始化结束)。因为在JAVA中有工具通过反射机制可以实现自动的序列化自定义类,于是抱着试一试的心态,开始寻找Objective-C中对应的方法。功夫不负苦心人,一位stackoverflow上的仁兄的回复提醒了我,iOS中的有Runtime Programming这样一种技术,通过阅读相应的文档,最终我找到了解决的方法。

    iOS的Runtime Programming中提供了一系列强大的方法在运行时对类进行操作,比如获取类的属性信息,类的协议信息,甚至是修改,增加,删除类的方法。对于我的需求而言,能够获取类的所有属性信息已经足够了。实际上我们需要解决的问题,就是动态的获取一个类中所有的属性名,只要能够获取这个,再通过这些属性名找到对应的属性值,最终把这些名-值建立成对,放入一个NSDictionary中,就可以使用第三方工具完成序列化的工作了。

    想到这里,可以说要做什么已经清楚了,接下来就是实干!我用苹果的官方文档给的例子,写了一个获取一个类所有属性名的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
        Foo *foo = [[Foo alloc] init];
         
        id fooClass = objc_getClass("Foo");
         
        unsigned int outCount, i;
         
        objc_property_t *properties = class_copyPropertyList(fooClass, &outCount);  //获取该类的所有属性
         
        for (i = 0; i < outCount; i++)
     
      {
             
            objc_property_t property = properties;
            
     
        //property_getName()返回属性的名字 在Foo中分别是 property1和property2
     
        //property_getAttributes()返回属性的属性,如是retain还是copy之类的
            
     
        //这个方法输出了该类所有的属性名与对应的属性的属性(好绕口啊)
     
        NSLog(@"%s %s\n", property_getName(property), property_getAttributes(property));  
             
        }


    我们知道,对于一个定义了@property的NSObject来说,只要调用与属性名相同名字的方法,便可以得到这个属性的值,如:[foo property1];会返回 @"haha" ,为了获取对应属性的值,我们只要把属性的名字用NSSelectorFromString()方法转换成selector,然后让这个类foo来调用就可以了。

    至此,可以说所有的难点都解决了,接下来就是把这个些东西组合起来,来生成NSDictionary了。下面的很简单,我就不写了:)
    最终序列化的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
        NSString *className = NSStringFromClass([theObject class]);
         
        const char *cClassName = [className UTF8String];
         
        id theClass = objc_getClass(cClassName);
         
        unsigned int outCount, i;
         
        objc_property_t *properties = class_copyPropertyList(theClass, &outCount);
         
        NSMutableArray *propertyNames = [[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];
             
            [propertyNames addObject:propertyNameString];
             
            [propertyNameString release];
             
            NSLog(@"%s %s\n", property_getName(property), property_getAttributes(property));
             
        }
         
        NSMutableDictionary *finalDict = [[NSMutableDictionary alloc] initWithCapacity:1];
         
        for(NSString *key in propertyNames)
        {
            SEL selector = NSSelectorFromString(key);
            id value = [theObject performSelector:selector];
             
            if (value == nil)
            {
                value = [NSNull null];
            }
             
            [finalDict setObject:value forKey:key];
        }
         
        [propertyNames release];
         
        NSString *retString = [[CJSONSerializer serializer] serializeDictionary:finalDict];
         
        [finalDict release];
         
        return retString;


    这里主要是提供一种思路,可能这种解决方法还会有些欠缺的地方,希望可以和大家一起讨论下。内容可能写的有点糙,如果有相关问题,欢迎留言询问。
    补充一点,这个方法我不确定能否通过苹果的审核,不过既然苹果的文档让用,我觉得应该没什么问题。
    之前排版有点问题,现在好了:) 
  • 相关阅读:
    iScroll.js 用法参考
    行内元素和块级元素
    struct和typedef struct彻底明白了
    C/C++语法知识:typedef struct 用法详解
    不是技术牛人,如何拿到国内IT巨头的Offer (转载)
    笔试客观题-----每天收集一点点
    <C++Primer>第四版 阅读笔记 第一部分 “基本语言”
    <C++Primer>第四版 阅读笔记 第四部分 “面向对象编程与泛型编程”
    <C++Primer>第四版 阅读笔记 第三部分 “类和数据抽象”
    <C++Primer>第四版 阅读笔记 第二部分 “容器和算法”
  • 原文地址:https://www.cnblogs.com/neozhu/p/2429556.html
Copyright © 2020-2023  润新知