22.5 注解的应用:定制序列化

22.5 注解的应用:定制序列化

在上一章,我们演示了一个简单的通用序列化类SimpleMapper,在将对象转换为字符串时,格式是固定的,本节演示如何对输出格式进行定制化。我们实现一个简单的类SimpleFormatter,它有一个方法:

1
public static String format(Object obj)

我们定义两个注解:@Label@Format@Label用于定制输出字段的名称,@Format用于定义日期类型的输出格式,它们的定义如下:

1
2
3
4
5
6
7
8
9
10
11
@Retention(RUNTIME)
@Target(FIELD)
public @interface Label {
String value() default "";
}
@Retention(RUNTIME)
@Target(FIELD)
public @interface Format {
String pattern() default "yyyy-MM-dd HH:mm:ss";
String timezone() default "GMT+8";
}

可以用这两个注解来修饰要序列化的类字段,比如:

1
2
3
4
5
6
7
8
9
static class Student {
@Label("姓名")
String name;
@Label("出生日期")
@Format(pattern="yyyy/MM/dd")
Date born;
@Label("分数")
double score;
//其他代码

我们可以这样来使用SimpleFormatter:

1
2
3
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Student zhangsan = new Student("张三", sdf.parse("1990-12-12"), 80.9d);
System.out.println(SimpleFormatter.format(zhangsan));

输出为:

1
2
3
姓名:张三
出生日期:1990/12/12
分数:80.9

可以看出,输出使用了自定义的字段名称和日期格式,SimpleFormatter.format()是怎么利用这些注解的呢?我们看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static String format(Object obj) {
try {
Class<? > cls = obj.getClass();
StringBuilder sb = new StringBuilder();
for(Field f : cls.getDeclaredFields()) {
if(! f.isAccessible()) {
f.setAccessible(true);
}
Label label = f.getAnnotation(Label.class);
String name = label ! = null ? label.value() : f.getName();
Object value = f.get(obj);
if(value ! = null && f.getType() == Date.class) {
value = formatDate(f, value);
}
sb.append(name + ":" + value + "\n");
}
return sb.toString();
}
catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

对于日期类型的字段,调用了formatDate,其代码为:

1
2
3
4
5
6
7
8
9
private static Object formatDate(Field f, Object value) {
Format format = f.getAnnotation(Format.class);
if(format ! = null) {
SimpleDateFormat sdf = new SimpleDateFormat(format.pattern());
sdf.setTimeZone(TimeZone.getTimeZone(format.timezone()));
return sdf.format(value);
}
return value;
}

这些代码都比较简单,我们就不解释了。