最近因为项目需求,需要将一些自定义的类序列化为JSON,网上有很多好用的第三方序列化工具,但都只能自动序列化一些基本类型,如NSNumber,NSString与NSDictionary这种,没有一种第三方工具提供直接将自定义类序列化的方法(至少据我所知:),而对于这种序列化自定义的类的需求,网上能查到的方法只有将自定义的类手动的转存为一个NSDictionary,然后再使用第三方工具来序列化。例如对于一个类Foo,有如下定义:
要序列化它的方法只有:
这种方法的缺陷在于太不灵活,每一次序列化的时候都需要写很多重复的 代码,上面的代码还没有考虑属性值为nil的情况(因为当属性值为nil时,NSDictionary会认为初始化结束)。因为在JAVA中有工具通过反射机制可以实现自动的序列化自定义类,于是抱着试一试的心态,开始寻找Objective-C中对应的方法。功夫不负苦心人,一位stackoverflow上的仁兄的回复提醒了我,iOS中的有Runtime Programming这样一种技术,通过阅读相应的文档,最终我找到了解决的方法。
iOS的Runtime Programming中提供了一系列强大的方法在运行时对类进行操作,比如获取类的属性信息,类的协议信息,甚至是修改,增加,删除类的方法。对于我的需求而言,能够获取类的所有属性信息已经足够了。实际上我们需要解决的 问题,就是动态的获取一个类中所有的属性名,只要能够获取这个,再通过这些属性名找到对应的属性值,最终把这些名-值建立成对,放入一个NSDictionary中,就可以使用第三方工具完成序列化的工作了。
想到这里,可以说要做什么已经清楚了,接下来就是实干!我用 苹果的官方文档给的例子,写了一个获取一个类所有属性名的方法:
我们知道,对于一个定义了@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
|
@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
];
[[JS*****erializer 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
]);
c*****t
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 = [[CJS*****erializer serializer] serializeDictionary:finalDict];
[finalDict release];
return
retString;
|
这里主要是提供一种思路,可能这种解决方法还会有些欠缺的地方,希望可以和大家一起讨论下。内容可能写的有点糙,如果有相关问题,欢迎留言询问。
补充一点,这个方法我不确定能否通过苹果的审核,不过既然苹果的文档让用,我觉得应该没什么问题。
之前排版有点问题,现在好了:)