自定义fastjson对枚举类型的序列化及反序列化过程
通常,fastjson在序列化及反序列化枚举时,一般以下几种策略:
1).根据枚举的name值序列化及反序列化(默认)
2).根据枚举的ordinal序列化及反序列化
3).根据枚举的toString方法序列化,但是反序列仍采取默认的策略
这显然对我们的业务处理不够灵活,考虑以下一种情况:
有一个文章类,它有标题,内容等属性,其中有一个属性是枚举类,表示文章是否通过审核。
如下:
public class Article {private String title; private String content;private AuditStatus status;public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; }public AuditStatus getStatus() { return status; } public void setStatus(AuditStatus status) { this.status = status; } }
对应的枚举类型,它包含一个标志状态的code:
public enum AuditStatus { AUDITING(1), PASSED(2), FAILED(3); private int code; AuditStatus(int code){ this.code = code; } public int getCode(){ return code; } public static AuditStatus convert(int code){ AuditStatus[] enums = AuditStatus.values(); for(AuditStatus e : enums){ if(e.code == code) return e; } return null; } }
注意,上述的code并不等于枚举的ordinal。
我们希望AuditStatus序列化和反序列化的都是根据其code值来。
比如序列化成如下字符串:
{"content":"This is content","status":1,"title":"Article 1"}
并能正确反序列化得到的Article对象中的status属性是AuditStatus.AuditStatus。
我们逐个对应上述的策略来看看是否能满足我们的要求:
方法一:会将status序列化成AUDITING。因此不行
方法二:会将status根据ordinal来序列化,得到的结果为0,也不行。
方法三:我们可以override toString方法,这样可以是的序列化的结果正确,但是反序列化过程仍然不行。
那么没有其他方法可以满足我们的业务需求了嘛?
网上给出的一种方法如下,修改Article类:
1 public class Article { 2 3 private String title; 4 5 private String content; 6 7 private AuditStatus statusCode; 8 9 public String getTitle() { 10 return title; 11 } 12 13 public void setTitle(String title) { 14 this.title = title; 15 } 16 17 public String getContent() { 18 return content; 19 } 20 21 public void setContent(String content) { 22 this.content = content; 23 } 24 25 @JSONField(serialize = false) 26 public AuditStatus getStatusCode() { 27 return statusCode; 28 } 29 30 @JSONField(deserialize = true) 31 public void setStatusCode(AuditStatus statusCode) { 32 this.statusCode = statusCode; 33 } 34 35 @JSONField(name = "status") 36 public int getStatus(){ 37 return statusCode.getCode(); 38 } 39 40 @JSONField(name = "status") 41 public AuditStatus setStatus(int code){ 42 return AuditStatus.convert(code); 43 } 44 }
这种方式屏蔽了默认的序列化及反序列化过程:行7(将status重命名成statusCode),行25-33(禁止了statusCode的序列化);并增加了自定义的序列化过程(行35-43)。
我测试了这种方式,虽然能在序列化时得到正常结果,但是却无法正常发序列化。
测试代码:
public class SerializeTest { public static void main(String[] args){ Article article = new Article(); article.setTitle("Article 1"); article.setContent("This is content"); article.setStatusCode(AuditStatus.AUDITING); String str = JSON.toJSONString(article); System.out.println(str); Article article1 = JSON.parseObject(str, Article.class); System.out.println(article1.getStatusCode()); } }
输出的结果:
因此这种做法并不可行,而且会让代码的可读性变差。
因此我推荐一种我尝试过成功的方法:编写自定义的编解码器,在通过@JsonField的serializeUsing和deserializeUsing属性指定编解码过程中使用的编解码器。
添加自定义的编解码器:
//ObjectSerializer和ObjectDeserializer分别是fastjson的编码器和解码器接口 public class AudtiStautsCodec implements ObjectSerializer, ObjectDeserializer { //反序列化过程 public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { Object value = parser.parse(); return value == null ? null : (T) AuditStatus.convert(TypeUtils.castToInt(value)); } //暂时还不清楚 public int getFastMatchToken() { return 0; } //序列化过程 public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { serializer.write(((AuditStatus)object).getCode()); } }
修改Article类,在status字段上增加@JsonField属性,并指定编解码器:
public class Article { private String title; private String content; @JSONField(serializeUsing = AudtiStautsCodec.class, deserializeUsing = AudtiStautsCodec.class) private AuditStatus status; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public AuditStatus getStatus() { return status; } public void setStatus(AuditStatus status) { this.status = status; } }
在通过之前的测试代码测试一下结果是否正确:
public class SerializeTest { public static void main(String[] args){ Article article = new Article(); article.setTitle("Article 1"); article.setContent("This is content"); article.setStatus(AuditStatus.AUDITING); String str = JSON.toJSONString(article); System.out.println(str); Article article1 = JSON.parseObject(str, Article.class); System.out.println(article1.getStatus()); } }
结果如下: